继承:
学习c++,你就一定会知道c++的三大特性,而说到三大特性,那么就会讲到继承。
继承的机制是最主要的就是为了能够实现代码复用,在原有的类的基础上增加功能进行拓展,产生新的类,这样的类就叫做派生类,也可以叫做子类;而原有的类就叫做基类,或者叫做父类。继承的定义格式很简单,只要在类后面加上:以及类访问限定符就行了。
继承关系和访问限定符:
主要关系分为三种,分别是大家熟悉的共有public和私有private,以及保护protected这三种。在这里需要强调的是protected的作用,当你在基类中声明一个成员为保护类型的时候,那么这个成员可以被派生类访问并使用,但是在类外则不可以进行访问。另外,当你使用这三种访问限定符的时候,你在使用public继承的方式时没有什么问题,但是当你使用另外两种的时候,你就需要注意,派生类中的成员的权限就被改变了:
基类中的public成员在protected和private两种访问方式下会降到protected和private两种权限;而protected成员会在private访问方式下降到private。
在大多数情况下,我们基本都是使用的public继承方式,保持is—a规则,每个父类的成员对于子类都可以使用。
在继承中,无论是哪一种继承方式,派生类都可以访问基类中的public成员和protected成员,而基类中的private成员无论是派生类还是类外都不可以访问。
派生类的默认成员函数:
但是,你需要注意的地方还是很多的,构造函数的调用顺序是从基类->派生类->构造函数体;以代码为例吧:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class B
{
public:
B()
{
cout << "B()" << endl;
}
~B()
{
cout << "~B()" << endl;
}
};
class C:public B
{
public:
C()
{
cout << "C()" << endl;
}
~C()
{
cout << "~C()" << endl;
}
};
void Test()
{
B b;
C c;
}
int main()
{
Test();
return 0;
}
你看完这个代码,你觉得代码的调用顺序是什么,仔细思考一下。
其实先调用b的构造函数是可以理解的,你只要还记得在构造函数中还有初始化列表这一东西。确实,代码会先进入C类的构造函数,但是在初始化列表中初始化的时候代码就会进入B类的构造函数中,此时还没有进入C类中构造函数的代码片段;而析构函数则是和构造函数相反的,所以就会得到上面的结果。
同名隐藏和赋值兼容规则:
同名隐藏,顾名思义,就是函数名或者是变量名相同,但是他们却分别处于不同的类中。举个例子,在基类中定义了一个Test()函数,而派生类中也定义了一个Test()函数,那么在类外调用的时候你只会访问到派生类中的成员,如果想要访问基类中的成员,那么就需要加上作用域限定符::来进行修饰,这样就可以访问到基类中的成员了。所以,友情提醒:在继承体系中最好不要定义同名的成员。
赋值兼容规则,主要就是在基类给派生类赋值或者是派生类给基类赋值时有一定的限制,规则如下:
派生类可以给基类赋值,单基类不能给派生类赋值;
父类的指针/引用可以指向子类,但子类的指针/引用不可以指向父类;但是这个不可以通过强转完成实现。
多继承和菱形继承:
多继承就是一个类继承了两个类或是多个类,而菱形继承就是两个类都继承了一个基类,而另外一个类则是多继承了这两个类。菱形继承中对象就会按照下图的方式分布:
虚拟继承:为了解决菱形继承的二义性问题
在继承的类前加上virtual关键字,这样可以通过在类中给上一个地址得到基类和派生类中的地址偏移量,只访问最基类中的成员,避免了两个类的基类中的数据成员的二义性。
此外,我在这里声明一下:友元关系不能被继承,但是静态成员变量可以被继承。