什么是继承?
简单的说,继承的使用就是为了代码复用。
继承:
①继承机制:是为了扩展原有类,增加新的功能。
②继承的定义格式:
子类名:继承方式 父类名
③继承方式有三种:private(私有继承) protected(保护继承) public(公有继承);
公有继承:基类中公有成员和保护成员在派生类中的访问权限不发生改变,基类中的私有成员在派生中是不可访问的,虽然继承下来了;
保护继承:基类中的公有成员和保护成员在派生类中都将修改为保护成员,基类中的私有成员在派生类中是不可访问的;
私有继承:基类中的公有成员和保护成员在派生类中都将修改为私有成员,基类中的私有成员在派生类中是不可访问的。
当基类中显式定义构造函数,派生类中也显式定义了构造函数:
构造函数的调用顺序:先调派生类的构造函数,在派生类的初始化列表中调基类的构造函数;
析构函数的调用顺序:先调派生类的析构函数(将派生类的析构函数体的内容执行完毕,在将要结束派生类的析构函数时调用基类的析构函数,将基类的析构函数调用完成后会返回到派生类析构函数体的左花括号之前,继而结束派生类的析构函数)。
注意的9点
①基类使用系统合成的构造函数(只有在需要时系统才会合成),这时的派生类也可以使用系统合成的构造函数(也是在需要时才会合成)。
当基类显式定义了缺省的构造函数,这时的派生类可以使用系统合成的(系统会在此时合成派生类的构造函数,在派生类的初始化列表会去调基类的构造函数)。
②当基类中定义了非缺省的构造函数(需要参数),这时派生类中必须显式定义构造函数,且在派生类构造函数的初始化列表需要调用基类的构造函数(传参),这里应该很容易明白,因为系统并不知道该给基类的构造函数传什么参数,所以你必须自己进行传参。
③基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。
④基类的私有成员在派生类中是不可访问的,如果基类不想在类外直接访问,但又想在派生类中可以访问时,需要定义为protected。可以看出,protected这个访问权限是为了继承才出现的。
⑤派生类会继承基类中的所有成员,只是访问权限有时会发生改变,导致基类的某些成员在派生类中不可见。
⑥每个类控制他自己的成员初始化过程。
⑦防止继承的发生:c++11新标准提供了一种防止继承发生的方法,即在类名后加上一个关键字final;
⑧使用关键字struct时—>默认公有继承 使用关键字class时—–>默认私有继承。
⑨公有继承是一个接口继承,每个子类对象都可以看成是一个父类对象。
赋值中需要要注意的几点
①子类对象可以赋值给父类对象(子类对象也可以看成一个父类对象);
②父类对象不能赋值给子类对象;
③子类对象的指针/引用不可以指向父类对象。(强制类型转换可以完成)
友元与静态
1.友元函数不能继承(因为友元函数不属于类,还记得友元函数不受类中访问权限的限制)。
2.静态成员变量可以继承,而且整个继承体系中只有一份静态成员变量,是所有类对象所共享的。
单继承.多继承与菱形继承
①单继承:一个子类只有一个直接父类时的这种继承关系为单继承。
②多继承:一个子类有两个或两个以上父类时的这种继承关系成为多继承。
多重继承的派生类继承了所有基类的属性。
③菱形继承(钻石继承)
菱形继承对象模型(对象成员在空间的布局):
定义一个D类的对象的d1,在D类对象中存放了两份_b——>造成二义性(当使用D类对象去直接访问_b时,变量不明确)。
由此:可知菱形继承会造成二义性和数据冗余的问题。
为了解决这个问题,我们引入了虚继承。
虚继承:(为了解决菱形继承造成的二义性和数据冗余等问题)
虚继承的目的是令某个类做出声明,承诺愿意共享它的基类。
注意:虚继承和一般的继承在调用构造函数时有一点差别,虚拟继承会先压栈,然后调用构造函数,而一般的继承直接调用构造函数,可以看出将1压栈只是为了区分虚拟继承和一般的继承。
虚拟继承示例:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class A
{
public :
int _a;
};
class B:virtual public A
{
public :
int _b;
};
void Test1()
{
B b;
b._a = 1;
b._b = 2;
}
int main()
{
Test1();
return 0;
}
菱形虚拟继承:
示例:
class B
{
public:
int _b;
};
class C1 :virtual public B
{
public :
int _c1;
};
class C2 :virtual public B
{
public:
int _c2;
};
class D :public C1, public C2
{
public :
int _d;
};
void Test1()
{
D d1;
cout << sizeof(D) << endl;
d1._c1 = 0;
d1._c2 = 1;
d1._d = 2;
d1._b = 3;
}
int main()
{
Test1();
return 0;
}
测试一下菱形虚拟继承的程序,测试一下D类对象需要占用多少字节?
答案:24