目录
前言
父子关系,看名字就知道了,父类和子类之间的关系嘛
父子关系
一:子类(派生类)的构成
1、构造析构函数之外的所有成员变量和成员函数(因为构造析构都有低保嘛,你不写系统会给你,你继承有什么用呢)
2、可以添加新的成员(在父类的基础上新增东西)
3、派生类中添加的成员名和基类成员名可以相同,但一般不这么做(因为名字相同了,就会出现问题,相同名字的基类的成员名被隐藏了,而派生类的起作用)
但是只是被隐藏,但是不代表不可以访问
访问的话可以用 基类名::基类成员名
这样就可以访问同名的基类成员了
二:子类(派生类)和父类(基类)的关系
派生类对象是基类对象,但是基类对象不是派生类对象。(可以这么记忆:儿子是父亲生的,所以
是儿子应当属于父亲的一部分,所以派生类对象是基类对象,父亲肯定不是儿子生的,所以基类对象不是派生类对象)
可以这么理解:派生类也就是子类的对象,可以当成基类对象使用,这样没有一点问题,因为子类继承了父类的所有成员,能够用的东西它全部都继承了,那么父类能做到事情,它全部都能做,所以可以当成父类对象去用,而父类对象为什么不能当子类来做呢,因为子类有了自己的新发展,子类能干的事情,父类不一定能做的了,所以就不行
代码举例(请结合《什么是继承(一看就懂)的代码来阅读)
int main()
{
Father obj_fa;
Son obj_son;
obj_fa = obj_son;//父类对象可以接受子类对象(可以这么理解父辈胸怀大,不和小的计较,可以
//接受)
//obj_son=obj_fa;//子类对象不能接收父类对象(小的接受不了大的,对父辈有偏见,不理解)
Father*p_fa = NUll;
Son*p_son = NUll;
p_fa = &obj_fa;//基本操作:对应类型的指针存储对象的首地址
p_son = &obj_son;//基本操作:对应类型的指针存储对象的首地址
p_fa = &obj_son; .//父类指针可以指向子类对象(可以这样记忆:大的指小的,小的不能指大的)
//p_son=&obj_fa;//子类指针不能指向父类对象
return 0;
}
三:构造析构顺序
1、派生类对象在实例化的时候会调用基类的构造函数(只要是创建子类对象,父类必然有构造函数要被调用,肯定得先有爸爸才能有儿子嘛,放到程序中来说,子类数据从父类继承过来,那么你要初始化子类的这些数据成员的时候,它的数据成员有一些是从父类继承过来的嘛,那必须先把父类的那一部分初始化了,然后在初始化自己的嘛,那么怎么去初始化父类成员呢,当然用构造函数初始化嘛,难不成自己写一个嘛,创建子类对象就必须调用父类的构造函数,当然自己的也会调用,具体是在什么时候掉用的呢,实际上就是在子类对象成员初始化列表的时候调,你得在你子类对象还没有创建之前,先把父类的那一部分通过构造函数构造了)
2、如果是多继承会按照基类创建的顺序调用,你先创建的哪个基类,你就先调用哪个嘛
3、实现继承时,要确保基类有可以调用的构造函数
代码举例:
Son::Son(int v, int n)
:FA(), Father(v)//不管基类顺序列表初始化的时候谁在前面,基类谁先创建的,谁就先被调用构造函数
{
}
四:菱形继承
一:基本结构
1、类A有两个子类:类A_1和类A_2
2、类A_1和类A_2共同派生出类AA
二:问题分析
1、类AA继承两份类A的成员
2、不能明确访问继承自类A的成员
三:解决方案
1、类名::成员名 治标不治本(因为内存还是重复了,另外你根本不需要两份相同的东西,内存
还是浪费了,并且也一样会出现在使用的过程当中,你类名加作用域符加成员名,代码一多,你类名
容易搞错,这样就会出现问题了,数据就不准确了,会出现混淆,这个方法可以解决但是不好)
2:虚继承 药到病除 彻底根治(可以解决上述两个问题,这两个问题说到底还是出现在两份
同样的内存上面,导致浪费了一份
在继承方式的前面加上关键字virtual(可以消除内存重复的问题)
代码举例:
class A_1 virtual public A
{
int a1;
};
class A_2 virtual public A
{
int a2;
};
总结
对于程序员来说,能不做的东西就不做