VC++ 之 虚基类详解
在上一节中,有两个身份证号显然是不合理的。为此,可以把class Person这个共同基类设置为虚基类,这样,从不同路径继承来的同名数据成员在内存中就只有一个拷贝,同名函数也只有一种映射。
虚基类定义方式
虚基类(virtual base class)定义方式如下:
class 派生类名:virtual 访问限定符 基类类名{...};
或:
class 派生类名:访问限定符 virtual 基类类名{...};
其中:virtual 关键字只对紧随其后的基类名起作用。
例如:
//学生类定义:
class Student::virtual public Person{...};
//教职工类定义:
class Employee::virtual public Person{...};
采用虚基类的多重继承的特点
采用虚基类后,在职研究生类对象的储存如下图所示。
与8.3节中的图8.4(b)不同的是:在Person的位置上放的是指针,两个指针都指向Person成员存储的内存。这种继承称为虚拟继承(virtual inheritance)。
采用虚基类的多重继承的构造与析构的次序
在派生类对象的创建中,构造次序如下:
- 虚基类的构造函数被调用,并按它们声明的顺序构造;
- 非虚基类的构造函数按它们声明的顺序调用;
- 成员对象的构造函数;
- 派生类自己的构造函数被调用。
析构的次序与构造的次序相反。
应用举例
【例8.3】在采用虚基类的多重继承中,构造与析构的次序。
//【例8.3】在采用虚基类的多重继承中,构造与析构的次序。 #include<iostream> using namespace std; class Object{ public: Object(){cout<<"constructor Object\n";} ~Object(){cout<<"deconstructor Object\n";} }; class Bclass1{ public: Bclass1(){cout<<"constructor Bclass1\n";} ~Bclass1(){cout<<"deconstructor Bclass1\n";} }; class Bclass2{ public: Bclass2(){cout<<"constructor Bclass2\n";} ~Bclass2(){cout<<"deconstructor Bclass2\n";} }; class Bclass3{ public: Bclass3(){cout<<"constructor Bclass3\n";} ~Bclass3(){cout<<"deconstructor Bclass3\n";} }; class Dclass:public Bclass1,virtual Bclass3,virtual Bclass2{ Object object; public: Dclass():object(),Bclass2(),Bclass3(),Bclass1(){cout<<"派生类建立!\n";} ~Dclass(){cout<<"派生类析构!\n";} }; int main(){ Dclass dd; cout<<"主程序运行!\n"; return 0; }
运行结果
Constructor Bclass3 //第一个虚拟基类,与派生类析构函数排列无关
Constructor Bclass2 //第二个虚拟基类
Constructor Bclass1 //非虚拟基类
Constructor Object //对象成员
派生类建立!
主程序运行!
派生类析构!
deconstructor Object //析构次序相反
deconstructor Bclass1
deconstructor Bclass2
deconstructor Bclass3
示例 虚基类在多层多重继承中的应用——在职研究生类定义
//【例8.4】虚基类在多层多重继承中的应用--在职研究生类定义。 #include<iostream> #include<string> using namespace std; enum Tsex{mid,man,woman}; //为简化,本例定义学生类时课程省略,并全部用string字符串 class Person{ string IdPerson; //身份证号 string Name; //姓名 Tsex Sex; //性别 int Birthday; //生日,格式1986年8月18日写作19860818 string HomeAddress; //家庭地址 public: Person(string, string,Tsex,int, string); Person(); ~Person(); void PrintPersonInfo(); //其他接口函数 }; Person::Person(string id, string name,Tsex sex,int birthday, string homeadd){ cout<<"构造Person"<<endl; IdPerson=id; Name=name; Sex=sex; Birthday=birthday; HomeAddress=homeadd; } Person::Person(){ cout<<"构造Person"<<endl; IdPerson='\0';Name='\0';Sex=mid; Birthday=0;HomeAddress='\0'; } Person::~Person(){ cout<<"析构Person"<<endl; } // IdPerson, Name, HomeAddress析构时自动调用它们自己的析构函数来释放内存空间 void Person::PrintPersonInfo(){ int i; cout<<"身份证号:"<<IdPerson<<'\n'<<"姓名:"<<Name<<'\n'<<"性别:"; if(Sex==man)cout<<"男"<<'\n'; else if(Sex==woman)cout<<"女"<<'\n'; else cout<<" "<<'\n'; cout<<"出生年月日:"; i=Birthday; cout<<i/10000<<"年"; i=i%10000; cout<<i/100<<"月"<<i%100<<"日"<<'\n'<<"家庭住址:"<<HomeAddress<<'\n'; } class Student:public virtual Person{ //以虚基类定义公有派生的学生类 string NoStudent; //学号 //30门课程与成绩略 public: Student(string id, string name,Tsex sex,int birthday, string homeadd, string nostud); //注意派生类构造函数声明方式 Student(); ~Student(){cout<<"析构Student"<<endl;} void PrintStudentInfo(); }; Student::Student(string id, string name,Tsex sex,int birthday, string homeadd, string nostud) :Person(id,name,sex,birthday,homeadd){ //注意Person参数表不用类型 cout<<"构造Student"<<endl; NoStudent=nostud; } Student::Student(){ //基类缺省的无参数构造函数不必显式给出 cout<<"构造Student"<<endl; } void Student::PrintStudentInfo(){ cout<<"学号:"<<NoStudent<<'\n'; PrintPersonInfo(); } class GStudent:public Student{ //以虚基类定义公有派生的研究生类 string NoGStudent; //研究生号 //其他略 public: GStudent(string id, string name,Tsex sex,int birthday, string homeadd, string nostud, string nogstudent); //注意派生类构造函数声明方式 GStudent(); ~GStudent(){cout<<"析构GStudent"<<endl;}; void PrintGStudentInfo(); }; GStudent::GStudent(string id, string name,Tsex sex, int birthday, string homeadd, string nostud, string nogstud) :Student(id,name,sex,birthday,homeadd,nostud),Person(id,name,sex,birthday,homeadd){ //因Person是虚基类,尽管不是直接基类,如定义GStudent对象,Person必须出现。 //不定义对象可不出现,为通用应出现。如不是虚基类,出现是错误的 cout<<"构造GStudent"<<endl; NoGStudent=nogstud; } GStudent::GStudent(){ //基类缺省的无参数构造函数不必显式给出 cout<<"构造GStudent"<<endl; } void GStudent::PrintGStudentInfo(){ cout<<"研究生号:"<<NoGStudent<<'\n'; PrintStudentInfo(); } class Employee:public virtual Person{ //以虚基类定义公有派生的教职工类 string NoEmployee; //教职工号 //其他略 public: Employee(string id, string name,Tsex sex,int birthday, string homeadd, string noempl); //注意派生类构造函数声明方式 Employee(); ~Employee(){cout<<"析构Employee"<<endl;} void PrintEmployeeInfo(); void PrintEmployeeInfo1(); //多重继承时避免重复打印虚基类Person的信息 }; Employee::Employee(string id, string name,Tsex sex,int birthday, string homeadd, string noempl) :Person(id,name,sex,birthday,homeadd){ //注意Person参数表可不用类型 cout<<"构造Employee"<<endl; NoEmployee=noempl; } Employee::Employee(){ //基类缺省的无参数构造函数不必显式给出 cout<<"构造Employee"<<endl; } void Employee::PrintEmployeeInfo(){ cout<<"教职工号:"<<NoEmployee<<'\n'; PrintPersonInfo(); } void Employee::PrintEmployeeInfo1(){cout<<"教职工号:"<<NoEmployee<<'\n';} class EGStudent:public Employee,public GStudent{ //以虚基类定义公有派生的在职研究生类 string NoEGStudent; //在职学习号 //其他略 public: EGStudent(string id, string name,Tsex sex,int birthday, string homeadd, string nostud, string nogstud, string noempl, string noegstud); //注意派生类构造函数声明方式 EGStudent(); ~EGStudent(){cout<<"析构EGStudent"<<endl;}; void PrintEGStudentInfo(); }; EGStudent::EGStudent(string id, string name,Tsex sex,int birthday, string homeadd, string nostud, string nogstud, string noempl, string noegstud) :GStudent(id,name,sex,birthday,homeadd,nostud,nogstud), Employee(id,name,sex,birthday,homeadd,noempl), Person(id,name,sex,birthday,homeadd){ //注意要定义EGStudent对象,Person必须出现 cout<<"构造EGStudent"<<endl; NoEGStudent=noegstud; } EGStudent::EGStudent(){ //基类缺省的无参数构造函数不必显式给出 cout<<"构造EGStudent"<<endl; } void EGStudent::PrintEGStudentInfo(){ cout<<"在职学习号:"<<NoEGStudent<<'\n'; PrintEmployeeInfo1(); //多重继承时避免重复打印虚基类Person的信息 PrintGStudentInfo(); // 虚基类Person的信息仅在GStudent中打印 } int main(void){ EGStudent egstu1("320102811226161","朱海鹏",man,19811226,"南京市黄浦路1号", "06000123", "034189","06283","030217"); egstu1.PrintEGStudentInfo(); GStudent gstu1("320102820818161","沈俊",man,19820818,"南京四牌楼2号", "08000312","058362"); gstu1.PrintGStudentInfo(); return 0; }
大学在册人员继承关系如下图所示:
图 大学在册人员继承关系
采用虚基类的在职研究生类的多重继承结构如下图所示:
运行时可以看到,尽管Employee和Student的构造函数都包含Person的构造函数,但并未真正调用。唯一的一次调用是在EGStudent构造函数中。