曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 3235|回复: 0
打印 上一主题 下一主题

虚基类

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2011-8-28 20:55:17 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
当一个类从多个基类中继承时可能会带来歧义。如下程序所示:
[C++] 纯文本查看 复制代码

#include <iostream>
using namespace std;

class base {
public:
    int i;
};

//derived1 从 base 中继承
class derived1 : public base {
public:
    int j;
};


//derived2 从 base 中继承
class derived2 : public base {
public:
    int k;
};

/*derived3 从 derived1 和 derived2 中继承,意味着 derived3 中有 2 个 base 的副本*/
class derived3 : public derived1, public derived2 {
public:
    int sum;
};

int main()
{
    derived3 ob;

    ob.i = 10;    //产生歧义,不知道选哪个 i
    ob.j = 20;
    ob.k = 30;

    //这里的 i 也不明确
    ob.sum = ob.i + ob.j + ob.k;

    //同样不明确到底用哪个 i
    cout << ob.i << " ";
    cout << ob.j << " " << ob.k << " ";
    cout << ob.sum;

    return 0;
}

编译时会出现歧义性提示:
$ g++ virbase.cc -o virbase
virbase.cc: In function ‘int main()’:
virbase.cc:32:5: error: request for member ‘i’ is ambiguous
virbase.cc:6:6: error: candidates are: int base::i
virbase.cc:6:6: error:                 int base::i
virbase.cc:37:14: error: request for member ‘i’ is ambiguous
virbase.cc:6:6: error: candidates are: int base::i
virbase.cc:6:6: error:                 int base::i
virbase.cc:40:13: error: request for member ‘i’ is ambiguous
virbase.cc:6:6: error: candidates are: int base::i
virbase.cc:6:6: error:                 int base::i
derived3 继承了 derived1 和 derived2,这样 derived3 中就有了两个 base 的副本,所以在使用 i 变量的地方就产生了歧义。根据编译时的错误信息提示,可以看到一种补救方法是使用“作用域运算符(::)”来手工的选择一个 i 。根据此,修改上面的程序为:
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;

class base {
public:
    int i;
};

//derived1 从 base 中继承
class derived1 : public base {
public:
    int j;
};


//derived2 从 base 中继承
class derived2 : public base {
public:
    int k;
};

class derived3 : public derived1, public derived2 {
public:
    int sum;
};

int main()
{
    derived3 ob;

    ob.derived1::i = 10;    //使用作用域运算符说明 i 来自 derived1 的副本
    ob.j = 20;
    ob.k = 30;
    ob.derived2::i = 40;

    ob.sum = ob.derived1::i + ob.j + ob.k;    //同样做了限定


    cout << "derived2::i =" << ob.derived2::i << endl;
    cout << "derived1::i = " << ob.derived1::i << " ";
    cout << "j = " << ob.j << " " << "k = " << ob.k << " ";
    cout << "sum = " << ob.sum << endl;

    return 0;
}

运行输出:
$ ./virbase
derived2::i =40
derived1::i = 10 j = 20 k = 30 sum = 60

如果派生类里的变量和所继承的基类中的变量同一名字会出现什么问题呢?
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;

class base {
public:
    int i;
};

class derived : public base {
public:
    int i;
    int k;
};

int main()
{
    derived ob;
    ob.i = 18;

    cout << ob.i << endl;
    return 0;
}

运行输出:
$ ./virbase2
18
那么这个 18 是 derived 里的 i 还是属于 base 里的呢?看下面的例子:
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;

class base {
public:
    int i;
};

class derived : public base {
public:
    int i;
    int k;
};

int main()
{
    derived ob;
    ob.i = 18;
    ob.base::i = 20;

    cout << ob.i << endl;
    cout << ob.base::i << endl;
    cout << ob.i << endl;
    return 0;
}

运行输出:
$ ./virbase2
18
20
18
由此可见,在只有一层继承的情况下,在没用用 "::" 运算符时,默认使用的是派生类中的变量,而基类中的同名变量被”隐藏“了起来,除非显式的说明这个 i 变量的从属,这种情况是不会产生歧义性的。

然而通过 "::" 运算符手工选择继承的类,也会带来一个更深层次的问题:如果在派生类中只需要一个 base 副本,那怎么办?有没有办法防止 derived3 中包含两个副本?解决这个问题的办法是通过虚基类来实现。

当两个或多个类从一个通用基类派生时,可以使用虚方式从基类继承,这种方式可以防止在派生类中产生一个基类的多个副本。要实现这种继承方式,可以在继承时,在基类名字的前面加上关键字 virtual

下面程序演示以虚方式继承基类:
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;

class base {
public:
    int i;
};

//derived1 从 base 中继承
class derived1 : virtual public base {
public:
    int j;
};


//derived2 从 base 中继承
class derived2 : virtual public base {
public:
    int k;
};

/*derived3 从 derived1 和 derived2 中继承,这次 derived3 中只有 base 的一个副本*/
class derived3 : public derived1, public derived2 {
public:
    int sum;
};

int main()
{
    derived3 ob;

    ob.i = 10;    //没有歧义
    ob.j = 20;
    ob.k = 30;
    ob.derived2::i = 40;
    
    //没有歧义
    ob.sum = ob.i + ob.j + ob.k;

    //没有歧义
    cout << ob.i << " ";

    cout << ob.j << " " << ob.k << " ";
    cout << ob.sum;

    return 0;
}

运行输出:
$ ./virbase3
40 20 30 90
上面程序中,关键字 virtual 被放在基类的名字前。derived1 和 derived2 都以虚方式从 base 中继承,那么在任何实际到它们的多重继承都将只会产生一个 base 副本。因此在 derived3 中也只有 base 的一个副本。

需要注意的是,尽管 derived1 和 derived2 都以虚方式从 base 中继承,但是二者的对象中仍然包含了 base 的副本,所以下面的语句是合法的:
derived1 myclass;
myclass.i = 88;

一个普通基类和一个虚基类的区别只有在一个类从多个基类下继承时才会显示出来。如果基类以虚方式被继承,那么在派生类中只有基类的一个副本,否则将有多个副本存在。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-5-18 03:16 , Processed in 0.079366 second(s), 24 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表