基本简介
在c++语言中。,一个派生类可以从一个基类派生,也可以从多个基类派生。从一个基类派生的继承称为单继承,从多个基类派生的继承称为多继承。继承可以使代码得到复用,子类还可以在父类的基础上添加功能
c++继承分为公有继承,私有继承,保护继承三类
单继承的定定义格式:
class <派生类名>:<继承方式><基类名>
{
<派生类新定义成员>
}
多继承的定定义格式:
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>…
{
<派生类新定义成员>
}
可见,多继承与单继承的区别从定义格式上看,主要是多继承的基类数量上的区别,如果省略继承方式,对‘class’将采用私有继承,对‘struct’将采用公有继承
继承方式
- 公有继承:
公有继承的特点是基类的共有成员和保护成员作为派生类的成员的时候,他们都保持原有的状态,而基类的私有成员仍然是私有的,不能被派生类访问。 - 私有继承
私有继承的特点是基类的共有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。 - 保护继承:
保护继承的提诶那是接力的所有共有成员和保护成员都称为派生类的保护成员,并且只能被它的派生类成员函数或者友元访问,基类的私有函数仍然是私有的。
下面列出三种不同的继承凡是的基类特性和派生类的特性
public | protected | privated | |
---|---|---|---|
公有继承 | public | protect | 不可见 |
私有继承 | private | private | 不可见 |
保护继承 | protected | protected | 不可见 |
为了进一步理解三种不同的继承方式在其成员的可见性方面的区别,下面从三种不同角度进行讨论。
公有方式
- 基类成员对其对象的可见性:公有成员可见,其他不可见。保护成员同私有成员。
- 基类成员对派生类的可见性:公有成员和保护成员可见,而私有成员不可见,保护成员同私有成员。
- 基类成员对派生类对象的可见性:公有成员可见,其他成员不可见。
所以,在公有继承时,派生类的对象可以访问基类中的公有函数;派生类的成员函数可以访问基类的共有成员和保护成员。这里,一定套区分清楚派生类的对象和派生类中的成员函数对基类的访问时不同的。
私有方式
- 基类成员对其对象的可见性:公有成员可见,其他不可见。
- 基类成员对派生类的可见性:公有成员和保护成员可见,而私有成员不可见。
- 基类成员对派生类对象的可见性:所有成员都不可见。
所以,在私有继承时,基类的成员只能由派生类中的成员访问,而且不玩再往下继承
保护方式
这种继承方式与私有继承情况相同,区别仅在于对派生类的成员而言,对基类成员有不同的可见性。
下面用一个实例代码来更加清晰的了解基类成员对其对象,对派生类,对派生类对象的可见性
#include<iostream>
using namespace std;
class base
{
base()
{
a=0;
b=1;
c=2;
}
int a;
protected:
int b;
private:
int c;
}
class derived1 : public base//公有继承
{
void fun()
{
cout<<base::a<<base::b<<base<<endl;//可以访问公有和保护
//cout<<base::c; //不能访问
}
}
class derived2 : private base//私有继承
{
void fun()
{
cout<<base::a<<base::b<<base<<endl;//可以访问公有和保护
//cout<<base::c; //不能访问
}
}
class derived3 : protected base//保护继承
{
void fun()
{
cout<<base::a<<base::b<<base<<endl;//可以访问公有和保护
//cout<<base::c; //不能访问
}
class derived4 : public derived3//不能被private继承的派生类的子类访问
{
void fun()
{
//cout<<base::a<<base::b<<base<<endl;
//cout<<base::c;
}
}
class derivated5 : base//默认是private继承
{
void fun()
{
cout<<base::a<<base::b<<endl;
//cout<<base::c;
}
}
int main()
{
base b1;
derived1 d1;
derived2 d2;
derived3 d3;
d1.fun();
d2.fun();
d3.fun();
cout<<b1.a//base class object 只能访问public member
cout<<d1.a//public 继承是derived class object 只能访问base class的public member
//cout<<d1.b<<d1.c;//不能访问
//cout<<d2.a<<d2.b//protected 继承,derived class object 不能访问base class member
//cout<<d3.a;//private继承时 derived class object 不能访问 base class的member
return 0;
}
}
关于菱形继承(虚继承)
虚继承是一个基类(base)派生出两个类(derived1,derived2)之后有一个类(dderived)继承derived1和derived2才使用的,如果按照原来的继承进行,当需要调用到base类的成员的时候,编译器会报错,因为编译器不知道是通过derived1去调用base还是通过derived2去调用
面对上面的问题,我们的解决办法是
直接在dderived中需要base时,用一个对象去指定是通过derived1还是derived2去调用base
但是这种方法存在缺陷,浪费了一个derived的空间不说(冗余),dderived存在两个base成员(存在二义性问题),和需求不同,这个问题就比较严重了。
之后就引入了虚函数的概念
只需要在两个derived1和derived2 定义时前面加上关键字virtual就变成了虚拟继承
内存分布就变成:
derived1 虚表指针
derived1成员
derived2 虚表指针
derived2 成员
dderived 成员
base 成员
虚表指针里面存放虚函数的地址,如果子类存在将父类的虚函数重写,将会把这个函虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。如果基类有3个虚函数,那么基类的虚表中就有三项(虚函数地址),派生类也会有虚表,至少有三项,如果重写了相应的虚函数,那么虚表中的地址就会改变,指向自身的虚函数实现。如果派生类有自己的虚函数,那么虚表中就会添加该项。