前情回顾
在第五层中,我遇到了在C++中第二个重载,操作符重载,它与函数重载差别很大,对于操作符重载,本质其实为函数,只是将函数名换成了C++提供给程序员特定的名字,同时第五层的力量也掌握到了,要踏入第六层了…
继承
当脚接触到第六层的地面的时候,那到声音随之飘来:“这第六层可不简单,它的力量是面向对象的第二大特性——继承,希望你不要让我失望…”
🚄上章地址:第五层:C++中的运算符重载
继承的作用
继承是面向对象的三大特性之一,在C++中,有些类的关系如下图:
可以发现,这些类,下级的成员除了会拥有上级的特性,还拥有自己的特性,这个时候如果一个一个封装,会造成很多重复的代码,这里就可以用继承的力量,减少大量的重复代码。
继承的基本语法
在浏览一些网站的时候,比如:csdn、牛客,都会发现,它们都有公共的头部和底部,那我自己的csdn主页来说:
不一样的是中间的内容不相同,那如果用普通方法打印一下这个网页的基本内容会是什么样的?
#include<iostream> using namespace std; class A1 { public: void head() { cout << "博客主页头部" << endl; } void middle() { cout << "A1的博客内容" << endl; } void bottom() { cout << "博客主页底部" << endl; } }; class A2 { public: void head() { cout << "博客主页头部" << endl; } void middle() { cout << "A2的博客内容" << endl; } void bottom() { cout << "博客主页底部" << endl; } }; class A3 { public: void head() { cout << "博客主页头部" << endl; } void middle() { cout << "A2的博客内容" << endl; } void bottom() { cout << "博客主页底部" << endl; } }; void test1() { A1 a1; A2 a2; A3 a3; a1.head(); a1.middle(); a1.bottom(); cout << "=======================" << endl; a2.head(); a2.middle(); a2.bottom(); cout << "=======================" << endl; a3.head(); a3.middle(); a3.bottom(); } int main() { test1(); return 0; }
每个都封装成类,看到是可以进行正常的输出的,但是,可以发现,重复代码很多,这些是不必要的,那现在就可以试试继承的写法,继承的基本语法为:
class 子类 : 继承方式 父类
{
子类的特点
}
子类也可以被叫做派生类
父类也可以被叫做基类
普通用法中,可以将重复的代码,封装到父类当中,将每个子类特别的成员封装在子类里面,那继承方式是什么?
继承方式
继承方式一共有三种:
1.公共继承(public)
2.保护继承(protected)
3.私有继承(private)
公共继承
父类所有权限不变继承到子类当中,但是不能访问私有权限的成员
保护继承
父类除了私有权限的其他权限都变成保护权限继承到子类当中,私有权限内成员依然不能访问
私有继承
父类中所有权限变成私有权限继承到子类当中,私有权限内成员依然访问不到
那了解了继承方式后,怎么用继承的方式写出上面的网页呢:
#include<iostream> using namespace std; class A { public: void head() { cout << "博客主页头部" << endl; } void bottom() { cout << "博客主页底部" << endl; } }; class A1:public A { public: void middle() { cout << "A1的博客内容" << endl; } }; class A2 :public A { public: void middle() { cout << "A2的博客内容" << endl; } }; class A3: public A { public: void middle() { cout << "A2的博客内容" << endl; } }; void test1() { A1 a1; A2 a2; A3 a3; a1.head(); a1.middle(); a1.bottom(); cout << "=======================" << endl; a2.head(); a2.middle(); a2.bottom(); cout << "=======================" << endl; a3.head(); a3.middle(); a3.bottom(); } int main() { test1(); return 0; }
继承中的对象模型
从父类继承过来的成员,那些属于子类?在继承方式当中提到,私有权限是访问不到的?那是否继承到了子类当中?
#include<iostream> using namespace std; class A { public: int _a; protected: int _b; private: int _c; }; class A1 :public A { public: int _d; }; void test1() { A1 a1; cout << sizeof(a1) << endl; } int main() { test1(); return 0; }
可以看到,父类内三个int占12,子类一个int占4,加起来正好是16,而输出的结果也是16,说明,父类内所有非静态成员会被继承到子类当中,只是父类内私有权限的成员被编译器藏起来了,不能进行访问,但是确实继承到了子类当中。
继承中的构造和析构顺序
那对于构造和析构呢?是先有的父类还是子类?可以试着用代码进行验证:
#include<iostream> using namespace std; class A { public: A() { cout << "父类构造调用" << endl; } ~A() { cout << "父类析构调用" << endl; } }; class A1:public A { public: A1() { cout << "子类构造调用" << endl; } ~A1() { cout << "子类析构调用" << endl; } }; void test1() { A1 a1; } int main() { test1(); return 0; }
可以看到,构造为先有父后由子,析构则反过来。
继承中同名成员访问
非静态成员
当子类和父类中出现了非静态同名成员,如何通过子类对象,访问子类或者父类中的非静态同名成员呢?
子类:直接访问
父类:需要加作用域
这里用代码进行验证:
#include<iostream> using namespace std; class A { public: void a() { cout << "父类中调用" << endl; } }; class A1:public A { public: void a() { cout << "子类中调用" << endl; } }; void test1() { A1 a1; a1.a(); a1.A::a(); } int main() { test1(); return 0; }
成员属性的调用也与函数调用一样,可以用代码来进行验证:
#include<iostream> using namespace std; class A { public: A() { _a = 10; } void a() { cout << "父类中调用" << endl; } void a(int a) { cout << "父类第二个函数调用" << endl; } int _a; }; class A1:public A { public: A1() { _a = 20; } void a() { cout << "子类中调用" << endl; } int _a; }; void test1() { A1 a1; cout << a1._a << endl; cout << a1.A::_a << endl; } int main() { test1(); return 0; }
那如果在父类中,发生函数重载,子类能直接调用参数不同的那个函数吗?
、#include<iostream> using namespace std; class A { public: void a() { cout << "父类中调用" << endl; } void a(int a) { cout << "父类第二个函数调用" << endl; } }; class A1:public A { public: void a() { cout << "子类中调用" << endl; } }; void test1() { A1 a1; a1.a(10); a1.A::a(); } int main() { test1(); return 0; }
编译器直接报错,程序是跑不起来的,这里是因为,在子类中出现与父类同名的成员函数,子类的同名函数会直接隐藏掉父类中所有的同名成员函数。