C++语言学习(十六)——多继承
一、多继承简介
1、多继承简介
C++语言支持多继承,一个子类可以有多个父类,子类拥有所有父类的成员变量,子类继承所有父类的成员函数,子类对象可以当作任意父类对象使用。
2、多继承语法规则
class Derived : public BaseA,
public BaseB,
public BaseC
{
};
3、多继承派生类的内存布局
通过多重继承得到的派生类对象可能具有不同的地址。
#include <iostream>
using namespace std;
class BaseA
{
public:
BaseA(int a)
{
ma = a;
}
private:
int ma;
};
class BaseB
{
public:
BaseB(int b)
{
mb = b;
}
private:
int mb;
};
class Derived : public BaseA,public BaseB
{
public:
Derived(int a, int b, int c):BaseA(a),BaseB(b)
{
mc = c;
}
private:
int mc;
};
struct Test
{
int a;
int b;
int c;
};
int main(int argc, char *argv[])
{
Derived d(1,2,3);
cout << sizeof(d) << endl;//12
Test* p = (Test*)&d;
cout << p->a << endl;//1
cout << p->b << endl;//2
cout << p->c << endl;//3
cout << &p->a << endl;//1
cout << &p->b << endl;//2
cout << &p->c << endl;//3
BaseA* pa = &d;
BaseB* pb = &d;
//子类对象的地址、首位继承类的成员地址
cout << &d << endl;
cout << pa << endl;
cout << &p->a <<endl;
//子类对象的地址、次位继承类的成员地址
cout << pb << endl;
cout << &p->b << endl;
return 0;
}
上述代码中,Derived类对象的内存布局如下:
Derived类对象从基类继承而来的处成员变量将根据继承的声明顺序进行依次排布。基于赋值兼容原则,如果BaseA类型指针pa、BaseB类型指针pb都指向子类对象d,pa将得到BaseA基类成员变量ma的地址,即子类对象的地址;pb将得到BaseB类成员变量mb的地址;因此,pa与pb的地址不相同。
4、菱形多继承导致的成员冗余
上述类图中,Teacher类和Student类都会继承People的成员,Doctor会继承Teacher类和Student类的成员,因此Doctor将会有两份继承自顶层父类People的成员。
#include <iostream>
#include <string>
using namespace std;
class People
{
public:
People(string name, int age)
{
m_name = name;
m_age = age;
}
void print()
{
cout << "name: " << m_name
<< " age: " << m_age <<endl;
}
private:
string m_name;
int m_age;
};
class Teacher : public People
{
public:
Teacher(string name, int age):People(name, age)
{
}
};
class Student : public People
{
public:
Student(string name, int age):People(name, age)
{
}
};
class Doctor : public Teacher, public Student
{
public:
Doctor(string name, int age):
Teacher(name + "_1", age),
Student(name + "_2", age)
{
}
};
int main(int argc, char *argv[])
{
Doctor doc("Bauer", 30);
//doc.print();//error
//error: request for member 'print' is ambiguous
//Doctor继承了从Teacher,Student继承来的print函数。
//doc.People::print();//error
//error: 'People' is an ambiguous base of 'Doctor'
//People被继承了两次
doc.Teacher::print();//name:bauer_1 age:30
doc.Student::print();//name:bauer_2 age:30
return 0;
}
二、虚继承
1、虚继承简介
在多继承中,保存共同基类的多份同名成员,可以在不同的数据成员中分别存放不同的数据,但保留多份数据成员的拷贝,不仅占有较多的存储空间,增加了成员的冗余,还增加了访问的困难。C++提供了虚基类和虚继承机制,实现了在多继承中只保留一份共同成员。
C++对于菱形多继承导致的成员冗余问题的解决方案是使用虚继承。
虚继承中,中间层父类不再关注顶层父类的初始化,最终子类必须直接调用顶层父类的构造函数。
虚继承的语法如下:class 派生类名:virtual 继承方式 基类名
2、虚继承示例
#include <iostream>
#include <string>
using namespace std;
class People
{
public:
People(string name, int age)
{
m_name = name;
m_age = age;
}
void print()
{
cout << "name: " << m_name
<< " age: " << m_age <<endl;
}
private:
string m_name;
int m_age;
};
class Teacher : virtual public People
{
public:
Teacher(