一、继承的定义与方式
继承:就是派生类(子类)继承基类(父类)的所有成员函数和成员变量。派生类也可以在原来的基础上进行扩张新的够功能。
派生类=基类(成员函数+成员变量)+自己的(成员函数、变量)
有上面这个表得:
1、基类private成员在派生类中不管什么继承方式都是看不见的。不可见是指基类的私有成员还是被继承到派生类对象中,但是在语法上限制派生类对象不管在类里面还是类外面都是不能去访问的。
2、如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。
3、使用关键字class时默认的继承方式是private,使用关键字struct时默认的继承方式是public。
二、基类和派生类对象赋值转换
1、派生类对象可以赋值给基类的对象/基类的指针/基类的引用。这里形象说法切片或者切割。但基类对象不能赋值给派生类对象。
2、子类指针可以赋值给子类对象。子类可以赋给父类引用:引用的底层就是指针。
3、基类的指针可以通过强制类型转换赋值给派生类的指针。但是必须是基类的指针是指向派生类对象时才是安全的。(指针的类型决定其可以看多大空间)
三、继承中的作用域
1、子类和父类有同名成员,调用子类成员时候其将屏蔽父类的成员函数,这种情况叫隐藏,也叫重定义。
四、派生类的默认成员函数
构造函数:
子类构造函数先调用父类构造函数,在执行子类构造函数。
父类的成员变量不能直接在子类的构造和拷贝构造直接初始化,必须调用父类的构造和拷贝构造直接初始化
拷贝构造:
如果子类不显示调用父类的拷贝构造,则调用父类的默认构造函数。
如果子类显示调用父类的拷贝构造,就不会再去调用父类的默认构造函数。
赋值运算符:
父类的成员变量可以在子类的赋值运算符重载函数中直接赋值
如果是编译器默认生成的子类赋值运算符重载函数,会调用父类的赋值运算符重载函数进行赋值
析构函数:
不允许下显示调用父类的析构函数。而其它的构造,拷贝构造,运算符重载如果你显示定义了调用的话,计算机就不会再给你调用了;
如何实现一个不能被继承的类
构造函数私有化,派生类中调不到基类的构造函数。则无法继承。
c++11中给了一个关键词final禁止继承
五、继承与友元
友元关系不能被继承,也就是说基类友元不能访问子类私有和受保护成员。
六、继承与静态成员
static成员不属于某个对象,而属于所有对象共享
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员,无论派生出多少个子类,都只有一个static成员实例。
不管你用子类还是用父类去调用其指的都是一个同一块地址空间。
两种访问静态成员的方式:用对象去访问,用作用域去访问。
七、复杂的菱形继承及菱形虚拟继承
1、单继承:一个子类只有一个直接父类时称这个继承关系为单继承。
2、多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。
3、菱形继承:菱形继承是多继承的一种特殊情况。
菱形继承指:当有两个派生类同时继承了一个基类,然后在用这两个派生类再被另一个派生类给多继承了;
菱形继承的问题:Java,python里面就没有多继承,这样会有二义性和数据冗余无法明确知道访问的是哪一个类里面的成员。
虚拟继承解决数据冗余和二义性的原理。
这里用了虚基表来解决,虚基表:当前对象相对于基类部分的偏移量,通过偏移量相对于公共部分的偏移量。公共部分在对象模型的最下面:直接父类部分通过各自的虚基表中的偏移量。虚基指针指向的是虚基表的头,然后后面记录了其偏移量。
八、继承和组合
1、优先使用对象组合,而不是类继承。
2、继承允许你根据基类类的实现来定义派生类的实现。这种通过生成派生类的复用通常称为白箱复用。在继承方式中,基类的内部细节对子类可见。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依靠关系很强,耦合度高。
3、对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种风格被称为黑箱复用。因为对象的内部细节是不可见的。组合类之间没有很强的依赖关系,耦合度很低。优先使用对象组合有助于保持每个类被封装。
class Tire
{
protected:
string _brand="Michslin";//品牌
size_t size=17;//尺寸
}
//组合关系
class car
{
protected:
string _colour="白色";
string _num="陕A888";
Tire _t;//轮胎
};