1、语法
(1)原则(2)语法 2、成员的访问权限
(1)继承访问权限变化(2)子类内部访问父类成员(3)子类对象访问父类成员(4)子类对象访问父类成员访问限定符的变化(5)小结 3、继承关系的构造顺序
(1)顺序(2)同名隐藏规则 4、函数同名的情况总结
4.1 练习(1)派生类的对象可以赋值给基类的对象(2)派生类对象的地址赋值给基类的指针变量(3)派生类对象可以初始化基类的引用 5、多重继承6、钻石继承/菱形继承
6.1 概念6.2 问题16.3 问题26.4 测验 7、关于多重继承8、对象构造顺序总结
1、语法 (1)原则is-a
父类/子类
基类/派生类
class 派生类 : [访问限定符] 基类 {
成员
}
如果不写继承访问限定符,默认是private
2、成员的访问权限| public | protected | private | |
|---|---|---|---|
| 类成员函数 | √ | √ | × |
| 友元函数 | √ | √ | √ |
| 子类函数 | √ | √ | × |
| 类对象 | √ | × | × |
子类继承了父类所有的成员变量和成员函数。与访问限定符无关。访问限定符只是限制了访问
子类访问父类成员变量,把父类成员变量访问限定符,改为protected。
分为子类内部和子类对象两种访问方式
1、子类内部访问public继承的父类成员变量
#includeusing namespace std; class base{ public: int public_data; protected: int protected_data; private: int private_data; }; class Derive:public base{ public: void test(){ cout << public_data < 这个时候会提示编译出错,不能成功,因为子类Derive继承父类base的public,protected但是不能访问父类的private。
2、子类内部访问public继承的父类成员函数#includeusing namespace std; class base{ public: int public_data; void public_func(); protected: int protected_data; void protected_func(); private: int private_data; void private_func(); }; class Derive:public base{ public: void test(){ public_func(); protected_func(); private_func(); } }; 这个时候也会出现同样的错误,因为子类Derive是public的,所以不能访问父类中private的函数
(2)子类内部访问父类成员
public protected private public继承 √ √ × protected继承 √ √ × private继承 √ √ × 子类内部访问父类成员,只能访问public和protected成员
(3)子类对象访问父类成员
public protected private public继承 √ × × protected继承 × × × private继承 × × × 子类只有public继承父类的时候,才能访问父类的public成员,其他都不能访问,通常子类使用public继承子类
(4)子类对象访问父类成员访问限定符的变化
继承方式/父类成员 public protected private public继承 public protected 不可见 protected继承 protected protected 不可见 private继承 private private 不可见 (5)小结
1、public无论类内部还是类对象都可以访问
3、继承关系的构造顺序 (1)顺序
2、protected类对象不可访问,类内部与继承类的内部都可以访问
3、private只有类内部才可以访问1、派生类的构造函数与析构函数的调用顺序
2、派生类的构造函数调用顺序
子对象构造、成员变量构造、父对象构造的顺序
3、派生类的析构函数调用顺序
子对象析构、成员变量析构、父对象析构的顺序#includeusing std::cout; using std::endl; class Member{ public: Member(){ cout << "Member Init" < 运行结果:
Parent Init Member Init Son Init Son Destory Member Destory Parent Destory这样的输出结果是因为,Son需要先初始化自己的父类,然后自己的成员,最后是自己的类,然后开始析构,,从自己开始析构,然后是成员,最后是父类的析构。
(2)同名隐藏规则
没有默认构造函数的基类在派生类的初始化,必须在初始化列表中初始化概念:子类的成员函数与基类成员函数同名,子类的函数将会隐藏基类的所有同名函数
#includeusing namespace std; class base{ public: void show(int data){ cout << "base::show(" << data << ")"< 这时候编译出错,因为derive.show()调用的时候,父类中也有同名的函数,所以要想办法声明这个调用的show()是父类中的show()
修改成指针方式,试一下int main(){ Derive *pderive = new Derive; pderive->show(); pderive->show(123); }这个时候还是不行,所以需要想另外的办法来解决问题!
解决办法:
1、Derive类对象调用被隐藏的父类函数时,在函数前面加上父类限定符。例如:derive.show(123)-->derive.base::show(123) pderive->show(123)-->pderive->base::show(123)2、Derive类中的名称会隐藏base类中同名的名称,在public继承中我们可以通过using声明:
class Derive:public base { public: using base::show; void show(){ cout<<"Derive::show()"<隐藏背后原因是为防止在程序库或应用框架内建立新的derived class时从疏远的base class继承重载函数
4、函数同名的情况总结
名称 英语 重载 overload 重写(覆盖) override 隐藏 hide 赋值兼容规则
概念:在任何需要基类对象的地方都可以使用公有的派生类对象来代替。反之,不可。1、派生类的对象可以赋值给基类对象
base base; Derive derive; base = derive;对象切割:在赋值时舍弃派生类自己的成员,只进行基类数据成员的赋值
2、派生类的对象可以初始化基类的引用Derive derive; base& base = derive;3、派生类对象的地址可以赋值给指向基类的指针
指向基类对象的指针变量也可以指向派生类对象Derive derive; base* base = &derive;向上转换:派生类对象赋值给基类 向下转换:基类对象赋值给派生类4.1 练习 (1)派生类的对象可以赋值给基类的对象int main(){ base base; Derive derive; base = derive; base.show(123); }(2)派生类对象的地址赋值给基类的指针变量int main(){ base *pbase = new Derive; pbase->show(123); }指针访问派生类中由基类继承来的对象,不能访问派生类中的新成员
(3)派生类对象可以初始化基类的引用int main(){ Derive derive; base &base = derive; base.show(123); }引用访问派生类中由基类继承来的对象,不能访问派生类中的新成员
5、多重继承一个类可以同时继承多个父类的行为和特征功能。
逗号分割的基类列表class 类名 : public 基类1,public 基类2{ };6、钻石继承/菱形继承 6.1 概念两个子类继承同一个父类,而又有子类同时继承这两个子类
6.2 问题1下面B、C继承与A,D同时继承B、C,那么同时D的实例调用A的成员函数会有什么情况?
#includeusing namespace std; class A{ public: void test(); private: int id; }; void A::test(){ cout <<__func__< 输出结果:
A size:4 B size:4 C size:4 D size:8原因:
6.3 问题2
B与C都继承了A的成员函数test(),D同时继承了B与C,调用test()无法确定时B还是C的。
解决:
给调用的成员函数前加上访问限定符,明确指定调用成员函数所属的类d.B::test();或者d.C::test();不同途径继承来的同名的成员在内存中有不同的拷贝,造成数据不一致
解决:
虚继承和虚基类
虚继承和虚基类:
虚继承:在继承定义中包含了virtual关键字的继承关系
虚基类:在虚继承体系中的通过virtual继承而来的基类class 类名:public virtual 基类{ }虚基类是一个相对概念,在虚继承关系中,父类相对于子类是虚基类
6.4 测验写出下列程序的执行结果,并分析结果。如果编译出错,分析原因并写出解决方案
#includeusing namespace std; class base{ public: base(){ cout << "base constuct" << endl; } ~base(){ cout << "base destuct" < 运行结果:
base constuct Member constuct Derive constuct Derive destuct Member destuct base destuct7、关于多重继承1、什么是多重继承?同时继承多个父类
8、对象构造顺序总结
2、多重继承有什么危害?菱形继承/钻石继承
3、什么是菱形继承/钻石继承?多重继承的两个或多个父类具有相同的祖先类
4、菱形继承/钻石继承有什么危害?因为多重继承的两个或多个父类具有相同的祖先类,所以会有完全相同的属性或方法。因此当前多重继承有两份相同的属性和方法。使用时会出现冲突。
5、如何解决菱形继承/钻石继承导致的冲突?使用虚继承
6、什么是虚继承?父类在继承具有相同的祖先类时,加上virtual基本原则:
1、先父后子
2、从左到右
3、先虚后实
4、从上到下
5、由内及外
示例:#includeusing namespace std; #define SIMPLE_CLASS(name) class name{ public: name(){cout << #name << "Constructor" << endl;} ~name(){cout << #name << "Destructor" << endl;} }; SIMPLE_CLASS(base1) SIMPLE_CLASS(base2) SIMPLE_CLASS(base3) SIMPLE_CLASS(Vbase1) SIMPLE_CLASS(Vbase2) SIMPLE_CLASS(Vbase3) SIMPLE_CLASS(Member1) SIMPLE_CLASS(Member2) SIMPLE_CLASS(Member3) #undef SIMPLE_CLASS class Test:public base1, public base2, public base3, public virtual Vbase1, public virtual Vbase2, public virtual Vbase3{ public: Test(){cout << "Test Constructor" << endl;} ~Test(){cout << "Test Destructor" << endl;} private: Member1 m1; Member2 m2; Member3 m3; }; int main(){ Test t; } 运行结果:
Vbase1Constructor Vbase2Constructor Vbase3Constructor base1Constructor base2Constructor base3Constructor Member1Constructor Member2Constructor Member3Constructor Test Constructor Test Destructor Member3Destructor Member2Destructor Member1Destructor base3Destructor base2Destructor base1Destructor Vbase3Destructor Vbase2Destructor Vbase1Destructor



