虚基类

原文地址:虚基类 作者:月云鹤

 

1、 虚基类的作用

   从上面的介绍可知:如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。在引用这些同名的成员时,必须在派生类对象名后增加直接基类名,以避免产生二义性,使其惟一地标识一个成员,如c1.A::display()。

   在一个类中保留间接共同基类的多份同名成员,虽然有时是有必要的,可以在不同的 数据成员中分别存放不同的数据,也可以通过构造函数分别对它们进行初始化。但在大多数情况下,这种现象是人们不希望出现的。因为保留多份数据成员的拷贝,不仅占用较多的存储空间,还增加了访问这些成员时的困难,容易出错。而且在实际上,并不需要有多份拷贝。

   C++提供虚基类(virtual base class)的方法,使得在继承间接共同基类时只保留一份成员。假设类D是类B和类C公用派生类,而类B和类C又是类A的派生类,如图2l所示。

   设类A有数据成员data和成员函数fun,见图5.22(a)。派生类B和C分别从类A继承了data和fun,此外类B还增加了自己的数据成员data b,类C增加了数据成员data_c。如图 5.22(b)所示。如果不用虚基类,根据前面学过的知识,在类D中保留了类A成员data的两份拷贝。在图5.22(c)中表示为intB::data和int C::data。同样有两个同名的成员函数,表示为void B::fun()和void C::fun()。类B中增加的成员data_b和类C中增加的成员data c不同名,不必用类名限定。此外,类D还增加了自己的数据成员data_d和成员函数fun_d。

[转载]虚基类[转载]虚基类
图 21                               图 22

现在,将类A声明为虚基类,方法如下:

   class A       //声明基类A
      
{…};
   classB:virtual publicA //声明类B是类A的公用派生类,A是B的虚基类
      
{…};
   classC:virtual public A //声明类C是类A的公用派生类,A是C的虚基类
      
{…};

   注意:虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声 明的。因为一个基类可以在生成一个派生类时作为虚基类,而在生成另一个派生类时不作为虚基类。

声明虚基类的一般形式为
   class派生类名:virtual 继承方式 基类名

   即在声明派生类时,将关键字virtual加到相应的继承方式前面。经过这样的声明后,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次,也就是说,基类成员只保留一次。

    在派生类B和C中作了上面的虚基类声明后,派生类D中的成员如图5.23所示。
需要注意:为了保证虚基类在派生类中只继才一次,应当在该基类的所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承。如果像图5.24所示的那样,在派生类B和C中将类A声明为虚基类,而在派生类D中没有将类A声明为虚基类,则在派生类E中,虽然从类B和c路径派生的部分只保留一份基类成员,但从类D路径派生的部分还保留一份基类成员。

[转载]虚基类            [转载]虚基类
图23                         图24

2、虚基类的初始化

   如果在虚基类中定义了带参数的构造函数,而且没有定义默认构造函数,则在其所有派生类(包括直接派生或间接派生的派生类)中,通过构造函数的初始化表对虚基类进行初始化。

例如:
   
class A       //定义基类A
      
{ A(int i){}//基类构造函数,有一个参数
        
...};
   class B:virtual public A //A作为B的虚基类
      
{ B(int n):A(n){}    //B类构造函数,在初始化表中对虚基类初始化
        
…};
   class C:virtual publicA //A作为C的虚基类
      
{ C(int n):A(n)){}   //C类构造函数,在初始化表中对虚基类初始化
        
…};
   class D:public B,public C //类D的构造函数,在初始化表中对所有基类初始化
      
{ D(int n):A(n),B(n),C(n){}
        …};

注意:
  
 1、在定义类D的构造函数时,与以往使用的方法有所不同。

规定:在最后的派生类中不仅要负责对其直接基类进行初始化,还要负责对虚基类初始化。

   2、C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类(如类B和类c)对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。

3、虚基类的简单应用举例

   在Teacher类和Student类之上增加一个共同的基类Person,如图25所示。作为人员的一些基本数据都放在Person中,在Teacher类和Student类中再增加一些必要的数据。

#include <iostream>
#include <string>
using namespace std;

1、定义公共基类Person
class Person
   { public:
      Person(char *nam,char s,int a) //构造函数
       { strcpy(name,nam);sex=s;age=a;}
     protected:                  //保护成员
      
char name[20];
      char sex;
      int age; };

2、定义类Teacher
class Teacher:virtual public Person //声明Person为公用继承的虚基类
   
{ public:
      Teacher(char *nam,char s,int a,char *t):Person(nam,s,a) //构造函数
        
{ strcpy(title,t); }
     protected:        //保护成员
      
char title[10]; }; //职称

3、定义类Student
class Student:virtual public Person //声明Person为公用继承的虚基类
   
{ public:
      Student(char *nam,char s,int a,float sco): //构造函数
      
Person(nam,s,a),score(sco){}            //初始化表
     
protected:      //保护成员
      
float score; };//成绩

4、定义多重继承的派生类Graduate
class Graduate:public Teacher,public Student //声明Teacher和Student类为公用继承直接基类
   
{ public:
      Graduate(char *nam,char s,int a,char *t,float sco,float w): //构造函数
      
Person(nam,s,a),Teacher(nam,s,a,t),Student(nam,s,a,sco),wage(w){} //初始化表
      
void show( ) //输出研究生的有关数据
        
{ cout<<"name:"<<name<<endl;
          cout<<"age:"<<age<<endl;
          cout<<"sex:"<<sex<<endl;
          cout<<"score:"<<score<<endl;
          cout<<"title:"<<title<<endl;
          cout<<"wages:"<<wage<<endl; }
     private:
      float wage; }; //工资


int main( )
 { Graduate grad1("Wang-li",'f',24,"assistant",89.5,1234.5);
   grad1.show( );
   return 0; }

运行结果为
   name:Wang-li
   
age:24
   
Sex:T
   
score:89.5
   
title:assistant
   
wages:1234.5

说明:
   
1、Person类是表示一般人员属性的公用类,其中包括人员的基本数据,现在只包含了3个数据成员:name(姓名)、sex(性别)、age(年龄)。Teacher和Student类是Person的公用派生类,在Teacher类中增加title(职称),在Student类中增加score(成绩)。
Graduate(研究生)是Teacher类和Student类的派生类,在Graduate类中增加wage(津贴)。一个研究生应当包含以上全部数据。为简化程序,除了最后的派生类Graduate外,在其他类中均不包含成员函数。
   2、清注意各类的构造函数的写法。在Person类中定义了包含3个形参的构造函 数,用它对数据成员name、sex和age进行初始化。在Teacher和Student类的构造函数中,按规定要在初始化表中包含对基类的初始化,尽管对虚基类来说,编译系统不会由此调用基类的构造函数,但仍然应当按照派生类构造函数的统一格式书写。在最后派生类Graduate的构造函数中,既包括对虚基类构造函数的调用,也包括对其直接基类的初始化。
   3、在Graduate类中,只保留一份基类的成员,因此可以用Graduate类中的show函数引用Graduate类对象中的公共基类Person的数据成员name,sex,age的值,不需要加基类名和域运算符(::),不会产生二义性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值