【C++进阶】深入了解继承 文章目录1.继承与友元2. 继承与静态成员3. 多继承及其菱形继承问题3.1 继承模型3.2 虚继承3.3 多继承中指针偏移问题3.4 IO库中的菱形虚拟继承4. 继承和组合4.1 继承和组合点击跳转继承前置知识1.继承与友元友元关系不能继承也就是说基类友元不能访问派生类私有和保护成员。//编译器从前往后找所以加上前置声明classStudent;classPerson{public:friendvoidDisplay(constPersonp,constStudents);protected:string _name;// 姓名};classStudent:publicPerson{protected:int_stuNum;// 学号};voidDisplay(constPersonp,constStudents){coutp._nameendl;couts._stuNumendl;}intmain(){Person p;Student s;// 编译报错error C2248: “Student::_stuNum”: 无法访问 protected 成员// 解决方案Display也变成Student的友元即可Display(p,s);return0;}2. 继承与静态成员基类定义了static静态成员则整个继承体系里面只有一个这样的成员。无论派生出多少个派生类都只有一个static成员实例。classPerson{public:string _name;staticint_count;};intPerson::_count0;classStudent:publicPerson{protected:int_stuNum;};intmain(){Person p;Student s;// 这里的运行结果可以看到非静态成员_name的地址是不一样的// 说明派生类继承下来了父派生类对象各有一份coutp._nameendl;couts._nameendl;// 这里的运行结果可以看到静态成员_count的地址是一样的// 说明派生类和基类共用同一份静态成员coutp._countendl;couts._countendl;// 公有的情况下父派生类指定类域都可以访问静态成员coutPerson::_countendl;coutStudent::_countendl;return0;}3. 多继承及其菱形继承问题3.1 继承模型单继承一个派生类只有一个直接基类时称这个继承关系为单继承多继承一个派生类有两个或以上直接基类时称这个继承关系为多继承多继承对象在内存中的模型是先继承的基类在前面后面继承的基类在后面派生类成员在放到最后面。菱形继承菱形继承是多继承的一种特殊情况。菱形继承的问题从下面的对象成员模型构造可以看出菱形继承有数据冗余和二义性的问题在Assistant的对象中Person成员会有两份。支持多继承就一定会有菱形继承像Java就直接不支持多继承规避掉了这里的问题所以实践中我们也是不建议设计出菱形继承这样的模型的。classPerson{public:string _name;// 姓名};classStudent:publicPerson{protected:int_num;//学号};classTeacher:publicPerson{protected:int_id;// 职工编号};classAssistant:publicStudent,publicTeacher{protected:string _majorCourse;// 主修课程};intmain(){// 编译报错error C2385: 对“_name”的访问不明确Assistant a;a._namepeter;// 需要显示指定访问哪个基类的成员可以解决二义性问题但是数据冗余问题无法解决a.Student::_namexxx;a.Teacher::_nameyyy;return0;}3.2 虚继承很多人说C语法复杂其实多继承就是一个体现。有了多继承就存在菱形继承有了菱形继承就有菱形虚拟继承底层实现就很复杂性能也会有一些损失所以最好不要设计出菱形继承。classPerson{public:string _name;// 姓名/*int _tel; int _age; string _gender; string _address;*/// ...};// 使用虚继承Person类classStudent:virtualpublicPerson{protected:int_num;//学号};// 使用虚继承Person类classTeacher:virtualpublicPerson{protected:int_id;// 职工编号};// 教授助理classAssistant:publicStudent,publicTeacher{protected:string _majorCourse;// 主修课程};intmain(){// 使用虚继承可以解决数据冗余和二义性Assistant a;a._namepeter;return0;}解决办法下面这幅图也是菱形继承classPerson{public:Person(constchar*name):_name(name){}string _name;// 姓名};classStudent:virtualpublicPerson{public:Student(constchar*name,intnum):Person(name),_num(num){}protected:int_num;//学号};classTeacher:virtualpublicPerson{public:Teacher(constchar*name,intid):Person(name),_id(id){}protected:int_id;// 职⼯编号};// 不要去玩菱形继承classAssistant:publicStudent,publicTeacher{public:Assistant(constchar*name1,constchar*name2,constchar*name3):Person(name3),Student(name1,1),Teacher(name2,2){}protected:string _majorCourse;// 主修课程};intmain(){// 思考⼀下这⾥a对象中_name是张三, 李四, 王五中的哪⼀个Assistanta(张三,李四,王五);return0;}实际上结果输出王五因为会按照初始化列表顺序调用先调用Person类调用Student和Teacher类的构造函数时不会再次调用Person类的构造函数所以结果是王五3.3 多继承中指针偏移问题下面说法正确的是( )Ap1 p2 p3 Bp1 p2 p3 Cp1 p3 ! p2 Dp1 ! p2 ! p3classBase1{public:int_b1;};classBase2{public:int_b2;};classDerive:publicBase1,publicBase2{public:int_d;};intmain(){Derive d;Base1*p1d;Base2*p2d;Derive*p3d;return0;}选C3.4 IO库中的菱形虚拟继承templateclassCharT,classTraitsstd::char_traitsCharTclassbasic_ostream:virtualpublicstd::basic_iosCharT,Traits{};templateclassCharT,classTraitsstd::char_traitsCharTclassbasic_istream:virtualpublicstd::basic_iosCharT,Traits{};4. 继承和组合4.1 继承和组合public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。组合是一种has-a的关系。假设B组合了A每个B对象中都有一个A对象。继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言在继承方式中基类的内部细节对派生类可见。继承一定程度破坏了基类的封装基类的改变对派生类有很大的影响。派生类和基类间的依赖关系很强耦合度高。对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse)因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系耦合度低。优先使用对象组合有助于你保持每个类被封装。优先使用组合而不是继承。实际尽量多去用组合组合的耦合度低代码维护性好。不过也不太那么绝对类之间的关系就适合继承(is-a)那就用继承另外要实现多态也必须要继承。类之间的关系既适合用继承(is-a)也适合组合(has-a)就用组合。// Tire(轮胎)和Car(车)更符合has-a的关系classTire{protected:string _brandMichelin;// 品牌size_t _size17;// 尺寸};classCar{protected:string _colour白色;// 颜色string _num陕ABIT00;// 车牌号Tire _t1;// 轮胎Tire _t2;// 轮胎Tire _t3;// 轮胎Tire _t4;// 轮胎};classBMW:publicCar{public:voidDrive(){cout好开-操控endl;}};// Car和BMW/Benz更符合is-a的关系classBenz:publicCar{public:voidDrive(){cout好坐-舒适endl;}};templateclassTclassvector{};// stack和vector的关系既符合is-a也符合has-atemplateclassTclassstack:publicvectorT{};templateclassTclassstack{public:vectorT_v;};intmain(){return0;}