类继承
和直接复制源代码修改相比,继承的好处是减少测试。
基类:原始类,
派生类:继承类,基于基类丰富更多内容的类。
继承一般用公有继承,class 派生类名 : public 基类名{......};
公有继承,基类的公有部分变成派生类的公有部分,基类的私有部分,还是基类的私有部分,派生类只能通过基类的公有方法和保护方法访问。这也说明,数据的安全性。一旦定义为私有,终生私有,派生出来也不会有权读取。
派生类构造函数
派生类,必须创建自己的构造函数。但是,派生类不能直接访问基类的私有数据。所以,派生类又必须使用基类的构造函数来初始化。
可曾还记得:成员初始化列表?是不是在哪见过?

只能通过这种方式给基类初始化,因为,创建派生类,要先创建基类,因为派生类包括基类数据。所以,在派生类的构造函数初始化时,要先有基类对象,所以,通过成员初始化列表法,在派生类生成前,先生成基类给派生类对象使用。
派生类析构函数
先执行派生类的构造函数,再调用基类的析构函数。
使用派生类
派生类和基类的特殊关系:派生类可以使用基类的方法(当然,只能使用基类公有的方法)。
基类指针可以指向派生类的对象,但是,反过来不行。可以这么理解,派生类是基于基类来的,派生类的东西要多一些,基类的东西少一些,这样,基类有的,派生类一定有,而派生类有的东西基类不一定用,因此,用基类指针指向派生类对象,用这个指针调用方法时,一定是能存在的。而如果用派生类指针指向基类,派生类更强大,指向的东西派生类可能没有,因此就可能出错。因此,不允许用指向派生类的指针指向基类对象,但可以用指向基类的指针指向派生类对象。
这段比较别扭,从包含可被包含的角度理解,会容易些。就像你可以是你我去父亲的儿子,但不能反过来。
多态公有继承
多态(重载)公有继承,多态就是重载,重载更好理解一些,多态有点过于抽象,不好理解。
类里为啥还有重载?主要是因为,基类的方法A,如果派生类觉得这个函数不好用,但是又不能给个新名字,因为新名字可能就没这么方便理解了,所以,派生类就定义一个和基类方法名称一样的函数,但是呢,做的内容可能就不一样了,或者做的更好了。这个时间,就在派生类的重新定义了方法A。
当然 还有另一个方法,虚函数......
好了,这里回忆一下,我们学了几个概念了:内联函数,友函数、虚函数。这些函数为什么存在呢?
内联函数:方便提升高频调用某个方法的效率,就搞了个内联函数,inline 开头。
友函数:为了不是成员函数,还可以访问类的私有数据,就搞了个友函数,friend开头。
虚函数:为了方便派生类重载方法,搞了个虚函数。
虚函数的这个方便之处在于,还记得开头说的:可以将派生类对象赋值给指向基类的指针,但反过来不行。这个规则会带来一个问题,当基类指针赋值的是派生类对象时,那么当这个指针调用一个基类和派生类都有的函数(方法)时,这个指针到底是调用基类的方法,还是派生类的方法?理论上,基类的指针指向派生的对象,指针也只是调用这个派生对象里基类的方法(因为派生对象继承基类的方法)。因此,虚函数的意义就在于,在基类指针指向派生类对象时,这个指针按指向的对象调用方法。也就是说,基类指针指向派生类对象,就调用派生对象的方法,指向基类对象,就调用基类对象的方法。
所以, 一般,基类的析构函数也定义为虚函数,能够保证派生类对象退出时,会执行完派生类析构函数后,继续执行基类的析构函数。否则基类指向派生类对象时,基类析构函数不是虚函数时,就只会调用基类的析构函数,而不会调用派生类的析构函数。
所以,之所在这么复杂,都是因为"指向基类的指针可以指向派生类对象"造成的。
静态联编和动态联编
因为"指向基类的指针可以指向派生类对象"(万恶之源),导致调用函数的时候,在编译阶段不能确认是用哪个函数,因此,只有在运行的时候才能确定,到底调用的是哪人函数,这种让编译器在运行时才选择调用函数叫动态联编。
静态联编是指在编译阶段就已知调用函数,使用静态联编。
对非虚函数使用静态联编,对于虚函数使用动态联编。
至于动态联编的实现方法,可不不必知道。
总结
如果,在派生类中要重新定义,在基类中,该方法要定义为虚函数。
构造函数不能定义为虚函数,因为不可继承。
析构函数一般要定义为虚函数。
友元函数不能是虚函数,因为友元函数不是类的成员函数。
如果重新定义了继承的方法,要确保与原型完全相同(指特征标),返回类型需要根据新的类进行修改,保持与新类一致。
如果基类的方法有重载,在派生类中,要重新定义所有的基类重载版本,否则,派生类只能调用重新定义的几个版本,其它基类的版本将被隐藏,派生类不能用没有重新定义的版本。
访问控制(protected)
忘记protected这个成员吧,就当只有public和private这两种。protected没啥好处。
抽象基类
纯虚函数?好变态,虚函数还不够,还整出来一个纯虚函数。
纯虚函数:声明虚函数原型时,直接赋值=0;就是说这个虚函数是纯虚函数。
声明 纯虚函数,不需要为类实现他,等到谁继承他时,继承他的派生类再实现他。
包含纯虚函数的基类,就是一个抽象基类。这种类,不能直接创建对象,他只能被继承。
继承和动态内存分配
派生类与new
动态内存分配就是使用new方法了。
继承类,主要考虑就是基类里使用了new方法来对成员分配空间,这个时候,继承时,如何释放基类里new开辟的内存空间,复制和,和赋值时,怎么进行深度复制,而不是执行浅复制(只复制地址,而不复制地址指向的值,导致指向多个对象指向同一个内存这种混乱)。
因此,对于基类有使用new为成员开辟内存的情况,需要考虑用个函数:
析构函数,复制构造函数,重载赋值运算符。
分两种情况:
1、派生类没有使用new为自己的成员分配内存,不需要重新定义上面3个函数。
2、派生类使用new了。必须重新定义上述3个函数。
派生类如何使用基类友元函数
第1步、通过基类的友元函数访问
第2步、通过强制类型转换,将派生类对象转换为基类对象。传递为基类对象的友元函数。
总结
1、编译器会自动创建一些公有成员函数
自动创建的函数,表明很重要
默认构造函数。(自动创建的,就啥也不做),一般建议提供默认构造函数,不要让编译器自动创建。
复制构造函数。一般有4种情况使用复制构造函数:将一个对象初始化为同类的另一个对象;按值递给函数;按值返回对象;编译器生成临时对象。如果类声明里,有使用new分配内存时,要重新定义复制构造函数。
赋值运算符。同上关注是否有new,要不要重定义。
2、其它
转换函数:将类对象转换为其它类型时要定义转换函数,一般是转换为其它基础类型,如int,float,double等。
explicit:用于转换函数和构造函数,表示不允许隐式,必须显示调用。
按值和按引用传递对象,一般建议按引用传递。一个是因为效率,一个是因为虚函数。
按值返回和按引用返回对象,一般按引用返回。
const:在一个函数头,有3个地方可以使用const。开始,中间,和结尾,不同位置,限制的目标不同。开始是限制修改返回值,中间是限制不修改传递的参数,结尾是限制这个函数不能修改对象自己的成员。
类的派生是is-a的关系。
构造函数,析构函数,赋值函数不能被继承。