学习目标
1.继承的概念及定义
2.基类和派生类对象赋值转换(切片)
3.继承中的作用域(隐藏/重定义)
4.派生类的默认成员函数
5.继承与友元
6.继承与静态成员
7.菱形继承与菱形虚拟继承
8.总结
1.继承的概念及定义
1.1概念
继承: 它允许你创建一个新的类(称为子类或派生类),以便从一个或多个现有的类(称为父类或基类)继承属性和行为。
--作用: 使代码可以复用, 允许程序员在保持原有类特性的基础上进行扩展,增加功能.
示例:
//父类 class Person { public: void Print() { cout << "name:" << _name << endl; cout << "age:" << _age << endl; } protected: string _name = "peter"; // 姓名 int _age = 18; // 年龄 }; //子类 class Student : public Person //继承 { protected: int _stuid; // 学号 };
1.2定义
--定义格式
--访问限定符
--继承基类成员访问方式的变化
1.取小: 访问限定符遇到小的就变小 (public继承遇到private成员, 继承访问限定符变private)
2.使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public
3.基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的
2.基类和派生类对象赋值转换(切片)
派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片
或者切割。寓意把派生类中父类那部分切来赋值过去。不会产生临时变量
3.继承中的作用域(隐藏/重定义)
1.基类和派生类都有独立的作用域
2. 子类和父类中有同名成员,子类成员将屏蔽对父类同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
3. 如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
4.在实际中在继承体系里面最好不要定义同名的成员。
示例:
// B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏 class A { public: void fun() { cout << "func()" << endl; } }; class B : public A { public: void fun(int i) { A::fun(); cout << "func(int i)->" <<i<<endl; } };
4.派生类的默认成员函数
1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
5. 派生类对象初始化先调用基类构造再调派生类构造。
6. 派生类对象析构清理先调用派生类析构再调基类的析构。
7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同. 那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。--总结:1.自己初始化自己的,2.除了析构函数会自动调, 其它都需要显示调用, 先处理父类,再处理子类
5.继承与友元
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
6.继承与静态成员
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子
类,都只有一个static成员实例 。
7.菱形继承与菱形虚拟继承
1.菱形继承:
导致的问题: 二意性, 数据冗余
2.菱形虚拟继承
使用: 在派生类继承的时候加上virtual
class B : virtual public A { public: int _b; };
功能: 解决菱形继承的二义性, 数据冗余
原理: 使用虚基表,存放偏移量, 将两个重复的改为一个,实现共用
8.总结
继承和组合
1.继承: is-a
public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象
2.组合: has-a
组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象
3.使用的选择
实际尽量多去用组合。组合的耦合度低,代码维护性好。
要实现多态,必须要继承。类之间的关系可以用继承,可以用组合,就用组合。