zoukankan      html  css  js  c++  java
  • 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)。

      采用虚基类的多重继承的构造与析构的次序

    在派生类对象的创建中,构造次序如下:

    1. 虚基类的构造函数被调用,并按它们声明的顺序构造;
    2. 非虚基类的构造函数按它们声明的顺序调用;
    3. 成员对象的构造函数;
    4. 派生类自己的构造函数被调用。


    析构的次序与构造的次序相反。

      应用举例

    【例8.3】在采用虚基类的多重继承中,构造与析构的次序。

    //【例8.3】在采用虚基类的多重继承中,构造与析构的次序。
    #include<iostream>
    using namespace std;
    
    class Object{
    public:
        Object(){cout<<"constructor Object
    ";}
        ~Object(){cout<<"deconstructor Object
    ";}
    };
    class Bclass1{
    public:
        Bclass1(){cout<<"constructor Bclass1
    ";}
        ~Bclass1(){cout<<"deconstructor Bclass1
    ";}
    };
    class Bclass2{
    public:
        Bclass2(){cout<<"constructor Bclass2
    ";}
        ~Bclass2(){cout<<"deconstructor Bclass2
    ";}
    };
    class Bclass3{
    public:
        Bclass3(){cout<<"constructor Bclass3
    ";}
        ~Bclass3(){cout<<"deconstructor Bclass3
    ";}
    };
    class Dclass:public Bclass1,virtual Bclass3,virtual Bclass2{
        Object object;
    public:
        Dclass():object(),Bclass2(),Bclass3(),Bclass1(){cout<<"派生类建立!
    ";}
        ~Dclass(){cout<<"派生类析构!
    ";}
    };
    
    int main(){
        Dclass dd;
        cout<<"主程序运行!
    ";
        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='';Name='';Sex=mid;
        Birthday=0;HomeAddress='';
    }
    Person::~Person(){
        cout<<"析构Person"<<endl;
    } // IdPerson, Name, HomeAddress析构时自动调用它们自己的析构函数来释放内存空间
    void Person::PrintPersonInfo(){
        int i;
        cout<<"身份证号:"<<IdPerson<<'
    '<<"姓名:"<<Name<<'
    '<<"性别:";
        if(Sex==man)cout<<""<<'
    ';
        else if(Sex==woman)cout<<""<<'
    ';
             else cout<<" "<<'
    ';
        cout<<"出生年月日:";
        i=Birthday;
        cout<<i/10000<<"";
        i=i%10000;
        cout<<i/100<<""<<i%100<<""<<'
    '<<"家庭住址:"<<HomeAddress<<'
    ';
    }
    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<<'
    ';
        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<<'
    ';
        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<<'
    ';
        PrintPersonInfo();
    }
    void Employee::PrintEmployeeInfo1(){cout<<"教职工号:"<<NoEmployee<<'
    ';}
    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<<'
    ';
        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构造函数中。

  • 相关阅读:
    暴力+构造 Codeforces Round #283 (Div. 2) C. Removing Columns
    Help Jimmy ~poj-1661 基础DP
    POJ1015 && UVA
    FatMouse's Speed ~(基础DP)打印路径的上升子序列
    Max Sum Plus Plus
    Column Addition~DP(脑子抽了,当时没有想到)
    区间的连续段~ST表(模板题)
    Exponial~(欧拉函数)~(发呆题)
    wyh的数列~(坑爹题目)
    wyh的物品~(二分)
  • 原文地址:https://www.cnblogs.com/delphi2014/p/4062216.html
Copyright © 2011-2022 走看看