C++继承

本节介绍C++的继承和多态的语法

继承方法

一图解

20200221123718539

继承中的对象模型

一句话,在继承中,父类的私有成员只是被隐藏了,占存储空间,还是会被继承下去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Base{
public:
int a;
protected:
int b;
private:
int c;
};
class Son: public Base{
public:
int d;
};
void test(){
std::cout<<sizeof(Son)<<endl;
//输出16
}

构造和析构的顺序

构造和析构的顺序,先构造父类,再构造子类;先析构子类,最后再析构父类

同名成员处理

当子类和父类出现同名的成员时:

若访问子类同名成员,直接访问即可;若访问父类同名成员,需要加作用域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Base{
public:
Base(){
a=100;
}
int a;
};
class Son: public Base{
public:
Son(){
a=200;
}
int a;
};
void test(){
Son s;
std::cout<<s.a<<endl;
//输出200
std::cout<<s.Base::a<<endl;
//输出100
}

子父类同名函数调用也是同理,但需要注意的是:

当子类中出现与父类同名的成员函数时,子类的同名成员会隐藏掉父类中所有的同名成员函数(各种重载函数),如果想访问到父类中被隐藏的同名成员函数,需要加作用域(作用域原理同同名成员变量)

同名静态成员处理方式

静态成员和非静态成员出现同名,处理方式一致

若访问子类同名成员,直接访问即可;若访问父类同名成员,需要加作用域。

静态成员能够通过类名访问,详细语法在之前的CPP类的基础中的静态成员已经提及过,——->传送门

(类名: :成员名,来访问静态成员)

多继承语法

C++允许一个类继承多个类

语法:

1
class 子类 : 继承方式 父类1 , 继承方式 父类2 ...

多继承可能会引发父类中有同名成员出现,需要加作用域区分

(C++实际开发中不建议使用多继承)

总而言之,多继承中如果父类中出现了同名情况,子类使用时候要加作用域加以区分

菱形继承

菱形继承概念

两个派生类继承同一个基类,某个类同时继承这两个派生类,这种继承称为菱形继承,又称钻石继承。

菱形继承问题

1706619988650

举个例子:

在这张图里面,羊继承了动物的数据,同意驼也继承了一份同样的数据,当羊驼使用数据时,就会产生二义性,解决这个问题的关键在于羊驼取动物的数据时,只从羊或者驼中之一取一份。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A{
public:
int age;
};
class B :public A{

};
class C :public A{

};

class Bc :public B, public C{

};

void test(){
Bc s;
s.B::age=18;
s.C::age=28;
}

观察这个代码,虽然可以通过多继承的作用域加以区分,两个父类相同的数据,但是这样会在实际开发中产生歧义,浪费存储空间,如例子中的羊驼(Bc)的年龄是继承18还是28呢?

实际上,也应该是,这份数据,我们只要有一份就可以。

解决菱形继承

利用虚继承,解决菱形继承的问题

继承之前加上关键字virtual,变为虚继承

在这个例子中,A类称为 虚基类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A{
public:
int age;
};
class B :virtual public A{

};
class C :virtual public A{

};

class Bc :public B, public C{

};

void test(){
Bc s;
s.B::age=18;
s.C::age=28;
s.age=38;
}

应用虚继承后,age只有一份。在上面代码的例子中,age先后被赋值为18,28,最后再被改为38,数据只有一份。虚基础是怎么实现只继承一份数据的?其实是用了虚基类指针和虚基类表

虚基础原理

3c6066e66608a29a50657a7cffc0d26

在这张表中,ST从S和T类中继承了vbptr(virtual base pointer)虚基类指针,然后只有一份从虚基类(A)继承的值age。

c38ffa6b96452d3575201a5b78f25a4

vbptr指向vbtable(虚基类表),在这个例子中S的vbptr指向的vbtable里的偏移量为8,从vbptr地址处偏移8就能取到age的值,同理,T的vbptr指向的vbtable里的偏移量为4,从该vbptr地址处偏移4就同样能取到age的值,age的值唯一且是共享。