高质量程序设计指南c++/c语言(36)--虚继承

     先看虚虚继承的一个例子:ios是抽象基类,ostream和istream都虚继承自ios,而iostream又继承自ostream和istream,这样,ios在iostream中只有一份实例。

#include<iostream>
using namespace std;

class Root
{
public:
    Root()
    {
        cout << "Root()" << endl;
    }
};

class Level11: public virtual Root
{
public:
    Level11()
    {
        cout << "Level11()" << endl;
    }
};

class Level12: public virtual Root
{
public:
    Level12()
    {
        cout << "Level12()" << endl;
    }
};

class Level21:public Level11, public Level12
{
public:
    Level21()
    {
        cout << "Level21()" << endl;
    }
};

int main(void)
{
    Level21 level;

    return 0;
}

输出:

Root()
Level11()
Level12()
Level21()

上面只输出一次Root(),是因为虚基类的特殊的初始化。通常,每个类只初始化自己的直接基类,在应用于虚基类的时候,这个初始化策略就会失败。如果使用常规规则,就可能会多次初始化虚基类。类将沿着包含该虚基类的每个继承路径初始化。为了解决这个重复初始化问题,由最底层派生类的构造函数初始化虚基类。在上面的例子中,在创建Level21对象的时候,由Level21的构造函数来初始化Root类。当然,在创建Level12的时候,就由Level12的构造函数来初始化Root类。

#include<iostream>
using namespace std;

class Root
{
public:
    Root()
    {
        cout << "Root()" << endl;
    }
    Root(int data)
    {
        cout << "Root(int data)" << endl;
    }
    int rootdData;
};

class Level11: public virtual Root
{
public:
    Level11(int data):Root(data)
    {
        cout << "Level11(int data)" << endl;
    }
    int level11Data;
};

class Level12: public virtual Root
{
public:
    Level12(int data):Root(data)
    {
        cout << "Level12(int data)" << endl;
    }
    int level12Data;
};

class Level21:public Level11, public Level12
{
public:
    Level21(int data):Root(data), Level11(data), Level12(data)
    {
        cout << "Level21(int data):Root(data), Level11(data), Level12(data)" << endl;
    }
    Level21():Level11(10), Level12(10)    //与Level21():Root(), Level11(10), Level12(10)等价
    {
        cout << "Level21():Level11(data), Level12(data)" << endl;
    }
    int level21Data;
};

int main(void)
{
    Level21 a;
    cout << endl;

    Level21 b(100);

    return 0;
}
输出:
Root()
Level11(int data)
Level12(int data)
Level21():Level11(data), Level12(data)

Root(int data)
Level11(int data)
Level12(int data)
Level21(int data):Root(data), Level11(data), Level12(data)

1、构造函数与析构函数次序

无论虚基类出现在继承层次中的任何地方,总是在构造非虚基类之前构造虚基类。看下面毫无规律的TeddyBear:

class Character{};
class BookCharacter: public Character{};

class ZooAnimal{};
class Bear:public virtual ZooAnimal{};

class ToyAnimal{};

class TeddyBear:public BookCharacter, public Bear, public virtual ToyAnimal{};

当构造TeddyBear类的对象时,按声明次序检查直接基类,确定是否存在虚基类。例如,首先检查BookCharacter的继承子树,然后检查Bear的继承子树,最后检查ToyAnimal的继承子树。创建TeddyBear类的对象,其构造函数调用顺序如下:

1\ ZooAnimal()

2\ ToyAnimal

3\ Character

4\ BookCharacter

5\ TeddyBear

在这里,由最低派生类TeddyBear控制ZooAnimal和ToyAnimal的初始化。

在合成赋值运算符中也是按照这个顺序赋值。保证调用基类析构函数的次序与构造函数的调用次序正好相反。

2、虚基类成员的可见性

#include<iostream>
using namespace std;

class Root
{
public:
    Root()
    {
        cout << "Root()" << endl;
        x = 0;
    }
    int x;
};

class Level11: public virtual Root
{
public:
    Level11()
    {
        cout << "Level11()" << endl;
    }
};

class Level12: public virtual Root
{
public:
    Level12()
    {
        cout << "Level12()" << endl;
        x = 12;
    }
    int x;
};

class Level21:public Level11, public Level12
{
public:
    Level21()
    {
        cout << "Level21()" << endl;
    }
};

int main(void)
{
    Level21 level;
    level.x = 10;
    cout << level.Root::x << endl;
    cout << level.Level12::x << endl;

    return 0;
}

输出:
Root()
Level11()
Level12()
Level21()
0
10

     可以无二义性的直接访问共享虚基类中的成员。同样,如果只沿一个派生路径重定义来自虚基类的成员,则可以直接访问该重定义成员。在非虚继承情况下,两种访问都可能是二义性的。

     假定通过多个派生路径继承名为x的成员,有下面三种可能:

1、如果在每个路径中x表示同一虚基类成员,则没有二义性。

2、如果在某个路径中x是虚基类的成员,而在另一路径中x是后代派生类的成员,也没有二义性--特定派生类实例的优先级高于共享基类实例。

3、如果沿每个继承路径x表示后代派生类的不同成员,则该成员的直接访问是二义性的。

 

class Base
{
public:
    void bar(int){}
protected:
    int ival;
};

class Derived1: virtual public Base
{
public:
    void bar(char){}
    void foo(char){}
protected:
    char cval;
};

class Derived2: virtual public Base
{
public:
    void foo(int){}
protected:
    int ival;
    char cval;
};

class VMI: public Derived1, public Derived2
{
public:
    void test()
    {
        cval = 'a';  //error: Derived1::cval or Derived2::cval
                     //即使Derived1的cval变为private的,也会发生二义性错误
        ival = 100;  //ok: Derived2::ival
        bar('d');    //ok: Derived1::bar
        foo('d');    //error: Derived1::foo or Derived2::foo 首先发生名字查找,然后检查参数
                     //即使Derived2的foo变为private的,也会发生二义性错误
    }
};

 

posted on 2013-05-14 15:08  江在路上2  阅读(192)  评论(0编辑  收藏  举报