赋值兼容性原则
赋值兼容规则是指在需要基类对象的任何地方都可以使用公有派生类的对象来替代。
在共有public继承的条件下:
子类对象可以当作父类对象使用
子类对象可以直接赋值给父类对象。
子类对象可以直接初始化父类对象。
父类指针可以直接指向子类对象。
父类引用可以直接引用子类对象。
代码举例:
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
protected:
const char* name;
public:
Parent()
{
name = "Parent";
}
void print()
{
cout<<"Name: "<<name<<endl;
}
};
class Child : public Parent
{
protected:
int i;
public:
Child(int i)
{
this->name = "Child";
this->i = i;
}
};
int main(int argc, char *argv[])
{
Child child(1000);
Parent parent = child;//用子类赋值
Parent* pp = &child;//指向子类的指针
Parent& rp = child;//子类的引用
parent.print();
pp->print();
rp.print();//这三个函数的输出全部都为child
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
上面的程序中,打印出来的全部都是child,产生的都是子类。用子类进行赋值。没有执行父类当中的构造函数而是执行了父类的拷贝构造函数。
继承对象模型
类在C++编译器的内部可以理解为结构体。
子类是由父类成员叠加子类新成员得到的。
如下图所示:
继承 与 构造和析构
在子类对象构造的时候需要调用父类构造函数对其继承得来的成员进行初始化。
如下图所示:
在子类对象析构的时候需要调用父类析构函数对其继承得来的成员进行清理。
如下图所示:
子类对象在创建时会首先调用父类的构造函数。
父类构造函数执行结束后,执行子类的构造函数。
当父类的构造函数有参数时,需要在子类的初始化列表中显式调用。
析构函数调用的先后顺序与构造函数相反。
举例说明:
继承与组合的混搭
组合:类中的成员变量可以是其它类的对象。
如果一个类继承自父类并且有其它的对象作为成员,那么构造函数如何调用?
口诀:先父母,后客人,再自己。
如下图所示,调用顺序并非按照初始化列表所列:
在上面的程序中,实际的构造函数的调用顺序与初始化列表的顺序正好相反。现构造Parent父类的构造函数,然后再是o1对象的构造函数,最后是o2对象的。最后是调用自己的构造函数。
初始化列表中的顺序没有任何作用。
继承中有同名成员变量
当子类中定义的成员变量与父类中的成员变量同名时会发生什么?
当子类成员变量与父类成员变量同名时:
子类依然从父类继承同名成员。
在子类中通过 作用域分别符 :: 进行同名成员区分。
同名成员存储在内存中的不同位置。
如下图所示:
小结
子类对象可以当作父类对象使用。
子类对象在创建时需要调用父类构造函数进行初始化。
子类对象在销毁时需要调用父类析构函数进行清理。
先执行父类构造函数,再执行成员构造函数。
在继承中的析构顺序与构造顺序对称相反。
同名成员通过作用域分辨符进行区分。