在继承时,如果一个派生类D,从几个基类中继承来,如F1,F2(假设有两个类,也许有更多),而这些基类又继承自同一个基类M,那么在D中就会有关于M的双重拷贝,在D的对象中,如果使用成员或者函数,就要使用作用域标识符来辨别该变量或者函数来自F1 or F2. 虚基类使得D中关于M的双重拷贝可以在D中只存放一份.举个列子
示例代码如下:
#include <iostream>
using namespace std;
#include <cstdlib>
#include <cstring>
class M{
public:
int nv;
void fun(){
cout << "this is M" << endl;
}
};
class F1:virtual public M{
public:
int nv1;
void fun1(){
cout << "this is F1" << endl;
}
};
class F2:virtual public M{
public:
int nv2;
void fun2(){
cout << "this is F2" << endl;
}
};
class D:public F1,public F2{
public:
int nvd;
void fund(){
cout << "this is D" << endl;
}
};
int main(){
D d;
d.nv = 2;
d.fun();
system("pause");
}
如果不加入virtual 会报下面的错误:
如果不加入virtual 那么必须加入作用域标识符代码如下:
#include <iostream>
using namespace std;
#include <cstdlib>
#include <cstring>
class M{
public:
int nv;
void fun(){
cout << "this is M" << endl;
}
};
class F1: public M{
public:
int nv1;
void fun1(){
cout << "this is F1" << endl;
}
};
class F2: public M{
public:
int nv2;
void fun2(){
cout << "this is F2" << endl;
}
};
class D:public F1,public F2{
public:
int nvd;
void fund(){
cout << "this is D" << endl;
}
};
int main(){
D d;
d.F1::nv = 2;
d.F1::fun();
d.F2::nv = 3;
d.F2::fun();
system("pause");
}
关于public、protect、private关键字的用法:
public 声明的变量成员函数可以访问,类的对象也可以访问
public 方式的继承: 基类中公有变量和函数、保护变量和函数在子类中仍然是公有和保护型的,子类不能访问基类的私有变量和函数
protect 声明的变量,在该类中成员函数可以访问,类的对象不能访问
protect 方式的继承:基类中公有变量和函数、保护变量和函数在子类中变成保护型的,子类不能访问基类的私有变量和函数
private 声明的变量,在该类中成员函数可以访问,类的对象不能访问
private 方式的继承:基类中公有变量和函数、保护变量和函数在子类中变成私有型的,子类不能访问基类的私有变量和函数
protect 方式继承和private 方式继承的不同:
假设B以private 方式继承A,B作为基类以private 方式被C继承,那么A中公有和保护型变量和函数在B中全部变成私有的,在C以private 方式继承B时,C只能继承来自B中的public和protected 类型的变量和函数,A中的变量和函数C继承不到.
假设B以protected 方式继承A,那么A中的公有和保护变量及成员函数在B中以protected 形式存在,在B 被C以protected 或者private 方式继承时,C只能继承来自B中的public和protected 类型的变量和函数,其中包含了B从A继承的变量和函数.
关于派生类初始化基类的构造函数的顺序问题(派生类构造函数执行的一般次序问题):
(1)调用基类构造函数,调用顺序按照继承时的顺序从左到右
(2)调用内嵌成员对象的构造函数,调用顺序按照它们在类中的声明顺序
(3)派生类的构造函数体中的内容
实例代码如下:
#include <iostream>
using namespace std;
#include <cstdio>
#include <cstdlib>
class B1{
public :
B1(int i){cout << "this is B1" << endl;}
};
class B2{
public :
B2(int j){cout << "this is B2" << endl;}
};
class B3{
public :
B3(){cout << "this is B3" << endl;}
};
class C:public B2, public B1, public B3{
public :
C(int a,int b,int c,int d):B1(a),memberb2(b),memberb1(c),B2(b){
cout << "this is C" << endl;
}
private :
B1 memberb1;
B2 memberb2;
B3 memberb3;
};
int main(){
C obj(1,2,3,4);
system("pause");
}
虚基类及其派生类构造函数:
我们将建立对象时所指定的类称为当时的最远派生类.
例如该文章中建立D类的对象d就是最远派生类
建立一个对象时,如果这个对象含有从虚基类继承来的成员,则虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的,而且只有最远派生类的构造函数可以对虚基类的构造函数进行初始化工作,该派生类的其他基类的构造函数对虚基类的构造函数的初始化调用自动忽略,以本文中第一个图举例,M类中的成员是通过D类的构造函数进行初始化的,而不是通过F1、F2的构造函数进行初始化的。具体的例子见:P235,C++ 语言程序设计(第三版)