类继承
公有派生
派生类对象包含基类对象,基类的公有成员将成为派生类的公有成员,私有成员也会成为派生类的一部分,但不能直接访问,需要通过公有方法或保护方法间接地访问。
派生类的构造函数:
派生类中,构造函数是必须的。
由于不能够直接访问基类的私有成员,派生类的构造函数必须调用基类构造函数完成初始化。
创建派生类对象时,程序首先创建基类对象,C++中使用成员初始化列表语法完成这种工作。形如Apple::Apple(int a,float b):Fruit(a),这代表将参数中的a作为实参传递给基类构造函数,若省略成员初始化列表,将调用基类的默认构造函数。
另外一种方法是,使用基类的对象以及派生类的新成员作为参数,这将调用基类的复制构造函数。
析构函数:
释放对象时,先执行派生类的析构函数,再执行基类的析构函数。
派生类的析构函数应使用虚函数,否则,释放对象指针时将只释放指针的类型的析构函数,使用虚析构函数,将调用相应对象类型的析构函数,使用虚析构函数可以确保正确的析构函数序列被调用。
基类指针可以在不进行显示转换的情况下指向派生对象,基类引用可以在不进行显式类型转换的情况下引用派生对象。
因此,通过隐式复制构造函数中的基类引用参数,将派生类对象赋给或初始化基类对象。
只有构造函数才可以使用成员初始化列表方法,对于非构造函数,可以调用公有的基类方法。一般使用作用域解析运算符调用基类方法。
静态联编、动态联编
函数名联编: 将源代码中的函数调用解释为执行特定的函数代码块。
静态联编: 在编译的过程中进行联编。
动态联编: 在程序运行时进行联编。
动态联编增加了额外的处理开销,所以静态联编的效率更高,被设置为C++的默认选择。
虚函数virtual
在不使用vitrual时,编译器将根据指针或引用的类型来选择方法,若使用virtual,则根据指针或引用指向或引用的对象的类型来选择方法。
虚函数的工作原理: 编译器为每个对象添加一个隐藏成员,保存一个指向函数地址数组的指针,被称为虚函数表,虚函数表中存储虚函数的地址,派生类会在基类表的基础上,增加其新定义的虚函数。调用虚函数时,程序将执行对应的函数。
使用虚函数的弊端: 每个对象都将增大存储地址的空间。每个类都将创建一个虚函数地址表,每个函数调用前都需要到表中查找一次地址。
构造函数不能是虚函数,创建对象时会自动调用对应类型的构造函数,将其声明为虚函数没有意义。
基类的析构函数应该是虚函数,这会保证当派生类对象赋给基类指针时,能够正确调用析构函数。
友元不能是虚函数,因为友元不是类成员,若需要,可以在友元函数中使用虚成员函数。
派生类没有重新定义函数时,将默认使用函数的基类版本。若派生连中有重定义,则使用派生链中最新的版本。
重新定义将隐藏基类方法,若在派生类中修改了函数的参数或返回值,这将隐藏函数的基类方法。若重定义的方法是基类方法,则将隐藏所有同名的基类方法。因此
重新定义继承的方法,应当保证与原来的原型完全相同,但当基类的返回类型时基类引用或指针,可以修改为派生类的。这被称为返回型协变
若基类声明被重载了,应该在派生类中重新定义所有的基类版本。
抽象基类(ABC)
C++使用纯虚函数提供未实现的函数。纯虚函数声明的结尾处为=0。
当类中包含纯虚函数时,不能创建该类的对象。
至少使用一个纯虚函数的类是抽象基类。
继承和动态内存分配
派生类的默认构造函数总是要在执行自身的代码后调用基类析构函数。//??我没懂这段,P516最下面。
派生类的默认复制构造函数使用显式基类复制构造函数完成对派生对象中基类成员部分的复制。
当派生类中使用动态内存分配时:
必须为派生类定义显式析构函数、复制构造函数和赋值运算符。
复制构造函数必须调用基类中的复制构造函数处理private数据,使用成员初始化列表可以实现。
赋值运算符需要通过作用域解析运算符显式地调用基类的复制运算符完成赋值。