一、多继承
C++语言支持多继承,一个子类可以有多个父类,子类拥有所有父类的成员变量,子类继承所有父类的成员函数,子类对象可以当作任意父类对象使用。
class Derived : public BaseA,
public BaseB,
public BaseC
{
};
当然,当两个父类存在相同名称的成员函数,在子类中,可以通过【类名::成员函数名称】的方式来确定调用的是哪个父类的函数。
二、虚继承
多继承虽然很不错,但是有个致命的菱形继承问题,例如:
若此时在调用一个Doctor对象中,调用一个从People继承而来的方法时,将会编译错误,提示二义性。
#include <iostream>
using namespace std;
class People {
public:
void People_test() {
cout << "People_test" << endl;
}
};
class Teacher : public People {
public:
};
class Student : public People {
public:
};
class Doctor :public Teacher, public Student {
public:
};
int main()
{
Doctor d;
d.People_test();
return 0;
}
执行结果如下:
此时就需要一个虚继承,C++提供了虚基类和虚继承机制,实现了在多继承中只保留一份共同成员,完美解决了菱形多继承导致的成员冗余问题。
虚继承中,中间层父类不再关注顶层父类的初始化,最终子类必须直接调用顶层父类的构造函数。
虚继承的语法如下:class 派生类名:virtual 继承方式 基类名
现在只需要在上面Teacher以及Student继承People的前面加上virtual关键字声明为虚继承,则可以解决上述二义性问题。
到目前为止,我们调用的都是各个类的默认构造函数,但是如果此时,我们想要调用其带参参数时,会如何呢,代码如下:
#include <iostream>
using namespace std;
class People {
public:
void People_test() {
cout << "People_test" << endl;
}
People(int a) {
cout << "people have one param " << a << endl;
}
People() {
cout << "people default" << endl;
}
};
class Teacher :virtual public People {
public:
Teacher(int a, int b) : People(a) {
}
};
class Student :virtual public People {
public:
Student(int a, int b) : People(a) {
}
};
class Doctor :public Teacher, public Student {
public:
Doctor(int a, int b) : Teacher(a, b), Student(a, b) {
}
};
int main()
{
Doctor d(1,2);
return 0;
}
此时的运行结果是什么呢?此时的运行结果是:
可能你会问,明明在Teacher和Student类的初始化列表里面调用了People的带参构造函数,为什么没有得到调用呢,因为C++语言规定,在基类是虚的时候,将禁止信息通过中间类自动传递给基类,因此上述Teacher和Student类构造函数不会调用People的带参构造函数,而只会调用一次默认构造函数,但如果你不希望使用默认构造函数来构造虚基类对象,可以在子类中使用这样的初始化列表:
Doctor(int a, int b) :People(b), Teacher(a, b), Student(a, b) {
}
此时的运行结果为:
结论:如果类有虚基类,则除非只需使用该虚基类的默认构造函数,否则必须显式地调用该虚基类的你一个构造函数。
三、虚基类和支配
我们知道,使用非虚基类时,如果子类从不同父类那里继承来了同名成员,如果我们调用时候不用类名进行限定,将导致二义性。但是如果此时是虚基类,且调用时候不加类名进行限定,也可能不会导致二义性,因为在某些情况下,如果某个类的成员优先于其他类的成员,则使用它时,也不会导致二义性。那优先级如何判断呢?派生类中的名称优先于直接或间接祖先类的相同名称。
class People {
public:
void test() {
cout << "People test" << endl;
}
People(int a) {
cout << "people have one param " << a << endl;
}
People() {
cout << "people default" << endl;
}
};
class Teacher :virtual public People {
public:
Teacher(int a, int b) : People(a) {
}
void test() {
cout << "Teacher test" << endl;
}
};
class Student :virtual public People {
public:
Student(int a, int b) : People(a) {
}
};
class Doctor :public Teacher, public Student {
public:
Doctor(int a, int b) :People(b), Teacher(a, b), Student(a, b) {
}
};
int main()
{
Doctor d(1,2);
d.test();
return 0;
}
运行结果为:
因为Teacher类相对于People,与Student类更亲近,所以,此时调用的是Teacher累test函数,等你若此时在Student中在定义个test函数,将会导致二义性,因为Teacher和Student都不是对方的基类。另外,这个二义性和访问规则无关,即使Student中的test是私有的,但仍会导致二义性。