C++:继承[下篇]
记录我自己零开始学习C++。
目录
一、继承与友元
二、继承与静态成员
三、复杂的菱形继承及菱形虚拟继承
3.1 单继承:一个子类只有一个直接父类时称这个继承关系为单继承。
3.2 多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。
3.3.菱形继承:菱形继承是多继承的一种特殊情况。
3.4.解决方法
3.4.1访问成员变量有二义性时,可指定类域。
四、继承的总结和反思
一、继承与友元
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员 。
二、继承与静态成员
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。
class Person
{
public:Person() { ++_count; }
protected:string _name; // 姓名
public:static int _count; // 统计人的个数。
};int Person::_count = 0;class Student : public Person
{
protected:int _stuNum; // 学号
};int main()
{Person p;Student s;cout << &Person::_count << endl;cout << &Student::_count << endl;return 0;
}
在上述代码中,静态成员属于整个类(而非某个对象),派生类不会创建新的静态成员实例,而是与基类共享同一个静态成员。因此,Person::_count
和 Student::_count
本质上是同一个变量,输出的地址会完全相同。
三、复杂的菱形继承及菱形虚拟继承
3.1 单继承:一个子类只有一个直接父类时称这个继承关系为单继承。
3.2 多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。
3.3.菱形继承:菱形继承是多继承的一种特殊情况。
class Person
{
public:string _name; // 姓名int _id;int _tel;string _adress;
};class Student : public Person
{
protected:int _num; //学号
};class Teacher : public Person
{
protected:int _id; // 职工编号
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};int main()
{//数据冗余和二义性Assistant a;//a._name = "小李";//a._name = "李老师";//1.指定类域a.Student::_name = "小李";a.Teacher::_name = "李老师";return 0;
}
由监视窗口得出数据的冗余:
在Assistant的对象中Person成员会有两份。
3.4.解决方法
3.4.1访问成员变量有二义性时,可指定类域。
int main()
{//数据冗余和二义性Assistant a;//a._name = "小李";//a._name = "李老师";//1.指定类域a.Student::_name = "小李";a.Teacher::_name = "李老师";return 0;
}
4.2 虚拟继承可以解决菱形继承的二义性和数据冗余的问题
虚拟继承:在继承会造成冗余的类的那里加上关键字 virtual。
class Person
{
public:string _name; // 姓名int _id;int _tel;string _adress;
};class Student : virtual public Person
{
protected:int _num; //学号
};class Teacher : virtual public Person
{
protected:int _id; // 职工编号
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};int main()
{Assistant a;a.Student::_name = "小李";a._name = "小李";a._name = "李老师";return 0;
}
四、继承的总结和反思
1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱
形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设
计出菱形继承。否则在复杂度及性能上都有问题。
2. 多继承可以认为是C++的缺陷之一,很多后来的OO语言都没有多继承,如Java。
3. 继承和组合
(1)public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
(2)组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
(3)优先使用对象组合,而不是类继承 。
(4)继承:白箱复用,强耦合的 "is-a" 关系
继承的核心是 “基于基类定义派生类”,本质是一种 “is-a”(是一个)的关系(例如 “狗是一种动物”)。
白箱复用:派生类可以直接访问基类的内部细节(如 protected 成员),基类的实现对派生类是 “可见” 的。这种可视性虽然简化了派生类的实现(直接复用基类逻辑),但也破坏了基类的封装性—— 基类的私有实现本应被隐藏,却因继承被派生类间接依赖。
高耦合:派生类的实现严重依赖基类。如果基类的逻辑(如方法实现)发生变化,即使接口没变,派生类也可能出错;反之,修改派生类时也可能需要调整基类(尤其当基类设计不严谨时)。
(5) 组合:黑箱复用,低耦合的 "has-a" 关系
组合的核心是 “通过组装已有对象实现新功能”,本质是 “has-a”(有一个)的关系(例如 “汽车有一个发动机”)。
黑箱复用:被组合的对象通过 “接口” 与组合类交互,其内部实现对组合类完全隐藏(类似 “黑箱”)。组合类只关心被组合对象能做什么(接口),不关心怎么做(实现)。
低耦合:组合类与被组合对象之间仅通过接口依赖,彼此独立。即使被组合对象的内部实现大幅修改,只要接口不变,组合类就无需调整;反之,修改组合类也不会影响被组合对象。这种低耦合性极大提升了代码的可维护性和扩展性。