在c++语言中,当我们使用基类的引用(或指针)调用一个虚函数时将发生动态绑定。基类都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。任何构造函数之外的非静态函数都可以是虚函数。成员访问权限protected,派生类可以访问,其它不能访问。成员访问权限private,派生类也不能访问。15.2.1节 529当派生关系继承权限为public的时候,派生类隐式的拥有了基类public的方法。在派生类对象中含有与其基类对应的组成部分,这一事实是继承的关键所在。指向派生类的基类的引用可以调用派生类特有的方法吗?每个类控制它自己的成员初始化过程,也就是派生类中基类的成员需要使用基类构造函数去初始化。首先初始化基类的部分(即先调用基类的构造函数),然后按照初始化列表初始化派生类的部分。派生类可以访问基类的public和protected成员。不受继承权限的控制吗?????遵循基类的接口。从语法上来讲可以在派生类的构造函数体内,给基类的成员进行赋值,但是最好不要这么做。派生类应该遵循基类的接口,并通过调用基类的接口来实现基类成员的初始化。
继承与静态成员
基类定义的静态成员,在整个体系中只存在该成员的唯一定义。且遵循访问权限可以通过类名或者对象访问。派生类的声明不应该包含派生列表。如果某个类要被用作基类,那么这个类必须已经被定义而不是仅仅被声明。可以通过在类名后面加上final关键字,来阻止继承。也就是说加了final的类不可能成为其他类的基类。可以将一个派生类对象的指针,存储在基类的智能指针内。如果表达式既不是引用也不是指针,那么它的静态类型和动态类型是一致的。
不存在从基类向派生类的隐式转化。
即使一个基类的引用或者指针绑定在一个派生类上,我们也不能将基类转换为派生类。如果已知一个类型转换是安全的,那么我们可以通过static_cast来进行强转。派生类向基类的转换只对引用或者指针有效。当我们用一个派生类对象为一个基类对象初始化或者赋值时,只有该派生类对象中的基类部分会被拷贝移动或赋值,他的派生类部分将被忽略掉。
虚函数
对非虚函数的调用,以及通过对象(非指针或引用)进行调用,在编译时就确定下来了。基类中的虚函数在派生类中也是虚函数,当派生类覆盖了某个虚函数时,其参数类型和返回值必须和基类中的完全一致。如果派生类中的某个函数,与基类中某函数,函数名相同,但是形参不同。那么编译器会认为这不是对基类中虚函数的覆盖,而是全新的函数。为了防止意外的出错,可以将派生类中的函数加上override关键字来说明这是对基类中某虚函数的覆盖。编译时如果基类中没有这个虚函数那么就会报错。final用于说明它不能在被其派生类进行覆盖了。final和override放在形参类型以及尾置返回值之后。如果虚函数使用默认实参,则基类和派生类中的默认值最好一致。当使用基类指针调用虚函数时,会传入基类定义的默认实参,无论实际对象是不是基类。如果派生类和基类默认实参不一致,结果将于预期不符。
回避虚函数的机制
可以使用类型名进行强制调用。如下:
baseP
->Base
::func();
这种回避调用在编译时就可以处理。一般当派生类中定义的函数将要覆盖掉基类中的版本,但是又需要调用基类的版本来完成某些运算的时候,可以这么操作。如果忘了加作用域运算符,将会无限递归下去。
抽象基类
可以将某函数声明时加上=0来表示这是纯虚函数。包含纯虚函数的类,叫做抽象基类,抽象基类不能创建对象。只有他的派生类,实现了这个纯虚函数。才能够创建对象。我们可以为一个纯虚函数提供定义,但是只能在类的外部提供。构造函数首先运行基类的构造函数,然后运行派生类的构造函数。
访问控制与继承
protected对他的派生类和友元来说是可以访问的。派生类的成员或者友元,只能通过派生类对象来访问基类的protected成员,派生类对于一个基类对象的protected成员没有任何的访问特权。友元访问权限??????
公有私有和保护继承
派生访问说明符对于派生类的成员能够访问其基类的成员没有影响。派生访问说明符的目的是控制派生类用户对基类成员的访问权限。派生类向基类转换的可访问性,当继承关系为protected的时候,与private没有任何区别????友元与继承
友元关系不能继承,每个类负责控制各自成员的访问权限。但是基类的友元可以访问派生类中基类部分。 可以使用using改变个别成员的可访问性派生类只能为他能够访问的成员提供using声明。struct和class和唯一区别就是成员默认访问权限及默认继承权限不同
继承中类的作用域
派生类的作用域嵌套在基类的作用域之内。当一个名字在派生类无法解析时,编译器将会去基类作用域查找该名字的定义。基类的指针,就算指向派生类,也不能调用派生类特有的方法。
名字冲突与继承
当派生类的成员与基类的成员名称冲突时,派生类的成员会隐藏(不是覆盖)基类的成员。可以通过作用域运算符来使用隐藏成员。建议:除了覆盖继承而来的虚函数之外,派生类最好不要重用其他定义在基类中的名字。编译器查找是从指针或者引用所指向的静态类型开始,向继承的顶端进行查找。名字查找先于类型检查:
声明在派生类中的函数,并不会覆盖基类中的同名函数,而是仅仅隐藏了,即使函数形参不一致。可以使用作用域运算符来指定使用哪个版本的方法。
虚函数与作用域
虚函数的形参列表必须相同,负责不会发生覆盖,而是隐藏,会导致不能使用基类的引用或者指针调用派生类的方法。
覆盖重载的函数
可以使用using来避免所有的重载都需要写派生类自己的版本。
构造函数与拷贝控制
虚析构函数
必须将基类的析构函数定义成虚函数,当删除基类指针时,就可以动态的找到指针指向的动态类型,并调用其析构函数。如果基类的析构函数不是虚函数,则delete一个指向派生类的基类指针,将发生未定义行为。如果一个类定义了析构函数,即使它通过=default的形式使用了合成的版本,编译器也不会为这个类合成移动操作。
合成拷贝控制与继承
派生类的拷贝控制成员
使用基类的引用或者指针调用的虚函数不能是内联的。类的大小,不包含静态成员大小。