“物是人非事事休, 欲语泪先流”
猛戳订阅🍁🍁 👉 [C++详解专栏] 👈 🍁🍁
这里是目录
一、继承的概念以及定义
继承机制是面向对象程序设计使得代码可以复用的重要手段,它允许程序员在保持原有类的特性的基础上进行扩展,增加功能,这样产生新的类,为派生类。
继承呈现了面向对象程序设计的层次结构。提现到了由简单到复杂的认知过程。
以前接触的复用都是函数复用,继承是类设计层次的复用。
//父类classA{public:voidPrint(){
cout <<"_a"<< endl;}protected:int _a;};//子类classB:publicA{protected:int _b;};
注意:实例化出来的对象里面不存方法,只存成员变量。
1.继承方法
重点:
1.在实际中父类成员基本都是保护和共有
2.protected和private成员对于基类都是一样的。
3.protected成员对于子类来说是可以使用的,而private成员
不可以用
保护继承是在类内可以访问,在类外不能访问。
私有继承在派生类中都是不可见。但不可见得意思是:私有的成员变量在子类里面,但是语法限制你不能访问。
访问的权限是:保护继承,公有继承,取小的那个。
二、基类和派生类对象赋值转换
派生类对象可以赋值给基类的对象,指针,和基类的引用。这里有个形象的说法叫做切割或者切片。(也叫作向上转换)
普通对象之间赋值会产生临时对象。但这个过程是天然的,没有类型转换。(这个在C++中很重要)这个叫做赋值兼容转换。
注意:
引用的是子类的一部分别名。
指针是指向父类的一部分
三、继承中的作用域
基类 和 派生类 都会形成独立的作用域。
2.隐藏
隐藏:在不同的域中会有相同的名称的变量。这时子类会对父类进行隐藏,也叫作重定义。
隐藏的话想访问父类的变量,则需要加**类域限定符::**。
最好不要用重名的变量。
成员函数也会重名,成员函数和成员变量一样。都会构成隐藏。
只要成员函数名相同就会构成隐藏。
但有一点需要注意:函数重载要求在同一作用域。在同一作用域才探讨重载。重载需要函数名相同,参数不同。
但隐藏是在不同的作用域!
四、派生类的默认成员函数
默认的意思,就是我们不写,编译器自动生成的构造函数。
子类构造函数原则:
1.先调用父类的构造函数,来初始化继承自父类的成员。
2.然后再初始化自己的成员。
析构、拷贝构造,赋值重载也类似。也是以上的原则。
父类的成员一定要调父类的构造函数初始化。初始化列表就是调用默认构造函数的地方。
1.拷贝构造
先在初始化列表调用父类的默认构造函数,再初始化子类的成员。
2.赋值重载拷贝
注意子类的operator=和父类的构成了隐藏,要记得加类域限定符::
3.析构函数
析构函数比较特殊。这是C++特殊的地方
析构函数不用调用父类的析构函数。这里比较特殊,构成了隐藏。
父类的析构函数和子类的析构函数构成了隐藏关系。
因为1.由于多态的需要,析构函数统一处理成了destructor().
2.为了保证析构顺序,会先调用子类,再自动调用父类的析构函数,不需要我们显式的调用。
这跟我们的存储的顺序有关,先定义的后析构。
4.如何设计一个不能被继承的类?
不能创建子类的对象,因为创建子类的对象,就要调用父类的构造函数。但是父类的构造函数是一个私有的。所以这个类不能被继承。
//父类classA{private:A(){}};//子类classB:publicA{};intmain(){
B b;//错误。不能创建对象return0;}
五、继承与友元
友元不能继承,友元的关系不能继承下来。
你是父亲的朋友,但不一定是儿子的朋友。
六、继承的静态成员变量
父类的静态变量子类都会继承下来。
C++静态成员变量需要在类外定义和初始化。
//父类构造函数classPerson{public:Person(){++_count;}protected:
string _name;//静态成员变量public:staticint _count;};//C++静态成员变量一定要在类外进行定义和初始化。int Person::_count =0;classStudent:publicPerson{protected:int _stuNum;};classGraduate:publicStudent{protected:
string _course;};intmain(){
Student s1;
Student s2;
Student s3;
Graduate s4;
cout <<"人数:"<< Person::_count << endl;
Student::_count =0;
cout <<"人数:"<< Person::_count << endl;return0;}
七、多继承(了解)
单继承:一个子类只有一个直接父类叫做单继承。
多继承:一个子类有两个或两个以上的直接父类就叫做多继承。
1.菱形继承
菱形继承是多继承的一种特殊情况。它会产生数据冗余,和二义性,子类会同时继承两个父类的两份数据,造成了二义性。
因为这个缺点,所以java是没有多继承的。
2.虚继承
虚继承可以说是C++的缺陷了。
怎么解决菱形继承这个问题呢?
这个就叫做虚继承,在腰部的位置加上关键字virtual
这时候vs的监视窗口看到的东西已经不准了。
3.对象模型
普通多继承的对象模型。
虚继承
多了两个指针区域,每个指针四个字节,用来存储距离A位置的偏移量,用来计算A的位置。
重点理解就是,B和C有个A但是A没有存在B和C的位置,所以需要通过偏移量找到A。
A就叫做虚基类。
存放指针的区域的指针,叫做虚基表指针。
为什么要存储A的偏移量,因为d赋值给b,在赋值的时候,需要找A的位置,需要切片,切片需要通过偏移量计算A的位置。
八、面向对象三大特性
面向对象不止三大特性:还有java的反射、抽象。
什么是封装、继承、多态?这个问题是没有标准答案的。
1.封装
主要是和C语言对比,C语言数据和方法是分开的,而C++是放在一起的。
例如:1.C语言的Stack和C++的Stack对比。
狭义的:
封装更好。来讲:封装指的是 访问限定符+类的封装。
广义的封装:例如封装了容器底层结构。封装其实也是一种隐藏。
2.迭代器的设计:
如果没有迭代器,容器的访问只能暴露底层结构。暴露的话,使用复杂,使用成本很高。对使用者的要求也极高。你得懂二叉树,链表。假如有迭代器的话,使用非常的方便!
封装的好处:在不暴露底层结构情况下,提供统一的访问容器的方式。
3.优先级队列的设计。–适配器模式。
假如不用适配器,就要自己写一个stack,queue,priority_queue。
2.继承
继承的本质就是:类设计角度的复用。
继承就是在更高的维度来设计类。
继承是类与类之间的关系。
有些数据和方法是每个角色都有的。有些是独有的。都有的就是重复的。
继承就是把那些每个角色都有的数据和方法提取出来。然后做成一个类。
其他独立的数据和方法就可以单独作为一个类来继承。
版权归原作者 乔 巴 所有, 如有侵权,请联系我们删除。