C++继承(基础)

赋值兼容规则

      public继承方式下,派生类对象可以赋值给基类的对象/指针/引用,但是基类不能赋值给派生类。

  • 可以将派生类的对象看成是基类的对象   
  • 在使用基类对象的位置,都可以使用派生类对象进行替换
  • 基类的指针/引用可以指向派生类的对象

派生类的默认成员函数

若基类的构造函数存在:

  1. 基类为缺省的构造函数:如果派生类没有显式定义构造函数,编译器就生成一个默认的构造函数,要在生成默认的构造函数的初始化列表位置调用基类的构造函数,来完成基类成员的初始化。
  2. 基类为非缺省的构造函数:此时用户必须显式给派生类定义构造函数(否则不会通过编译),并且在其构造函数初始化列表的位置显式调用基类的构造函数,来完成基类成员的初始化。
  • 派生类的构造函数的初始化列表可以包含基类的构造函数、派生类成员的初始化,但是不能有基类成员的初始化!

        (因为构造函数是不可继承的。所以派生类的构造函数必须通过调用基类的构造函数初始化基类成员)

        若派生类中包含对象成员,则派生类的构造函数初始化成员列表中既要列出 基类的构造函数 也要列出 对象的构造函数。派生类定义对象时,先调用基类的构造函数,再调用对象的构造函数,最后调用派生类的构造函数。

  • 派生类对象构造的次序先完成对基类成员的构造,再初始化派生类自己的成员

        创建派生类对象时,先调用派生类的构造函数,在构造函数的初始化列表位置完成基类成员的初始化,调用基类的构造函数,然后初始化派生类自己的成员执行初始化语句,再来执行派生类构造函数的函数体。
        

  •  派生类对象析构的次序:先调用派生类析构再调基类的析构

        析构派生类的对象时,先调用派生类的析构函数,编译器会在派生类析构函数被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类 对象先清理派生类成员再清理基类成员的顺序。


  • C++11中防止类被继承,在类名后加 final  将基类构造函数私有化
  • 友元关系不能被继承,也就是说基类友元不能访问子类私有和保护成员
  • 静态成员:可以继承,并且整个继承体系中只有一份。

 

常见面试题:实现一个不能被继承的类。

// C++98中构造函数私有化,派生类中调不到基类的构造函数,则无法继承。
class NonInherit
{
public:
 static NonInherit GetInstance()
 {
 return NonInherit();
 }
private:
 NonInherit()
 {}
};


// C++11给出了新的关键字final禁止继承
class NonInherit final
{};

 


同名隐藏

定义:基类和派生类中具有相同名称的成员(成员变量/成员函数), 派生类会自动屏蔽基类中此成员,而优先访问派生类自己的成员,基类同名成员就被隐藏了。同名隐藏分为两类:类成员隐藏类成员函数隐藏。

  • 类成员隐藏:派生类和基类中有同名成员变量,派生类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。
//A中的_a与B中的_a构成隐藏关系
class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
public:
	int _a = 5;
};
class B : public A
{
public:
	void fun(int i)
	{
		A::fun();
		cout << "func(int i)->" << i << endl;
	}
public:
	int _a = 10;
};
void Test()
{
	B b;
	cout << b._a << endl;
};

运行程序:

   

由这个例子我们发现:派生类成员B在访问与基类成员A同名的成员_a时,直接访问了自己的类成员_a,而屏蔽了基类对同名成员的直接访问,印证上述定义。

注意:在实际中的继承体系里面最好不要定义同名的成员,否则非常容易混淆。

 

  • 类成员函数隐藏:只要函数名相同就构成隐藏。(在派生类成员函数中,可以使用 基类::基类成员 显示访问)

按照定义,派生类与基类存在相同函数名的成员函数那么这个函数会不会发生函数重载呢?

答案是不会,因为函数重载需要在同一作用域内,而继承中派生类和基类有各自独立的作用域。

class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
public:
	int _a = 5;
};
class B : public A
{
public:
	void fun(int i)
	{
		cout << "func(int i)->" << i << endl;
	}
public:
	int _a = 10;
};
void Test()
{
	B b;
	b.fun(10);
};

 运行程序:

B中的fun和A中的fun构成隐藏,由此我们得出结论:成员函数满足函数名相同就构成隐藏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章