c++ 多重继承,an ambiguous base of

问题:

  1. 问题

在写代码时,碰到了一个问题。有一个类多重继承。该类无法用基类指针去指代它。引起的问题。

代码实例如下

#include <iostream>

using namespace std;

class Top
{
    public:
    Top(){}
	virtual ~Top()
    {}
	virtual void f()
	{
		cout<<"Top::f"<<endl;
	}

	virtual void g()
	{
		cout<<"Top::g()"<<endl;
	}
        int nData;
};

class Left: public Top
{
    public:
    Left(){

    }
    virtual ~Left()
    {

    }
	virtual void f()
	{
		cout<<"Left::f"<<endl;
	}

	virtual void g()
	{
		cout<<"Left::g()"<<endl;
	}
};

//int main()
//{
 //   //Left* t = new Left();
//    Top* t = new Left();
//    t->Top::f();
//}

class Right: public Top
{
    public:
    Right();
    ~Right();
	virtual void f()
	{
		cout<<"Right::f"<<endl;
	}

	virtual void g()
	{
		cout<<"Right::g()"<<endl;
	}
};

class Bottom: public Left, public Right
{
    public:
        Bottom();
        ~Bottom();
        virtual void f()
        {
            cout<<"Bottom::f"<<endl;
        }

        virtual void g()
        {
            cout<<"Bottom::g"<<endl;
        }
};

int main(){
    Top* t = new Bottom();
    return 0;
}

在我的ubuntu上。编译时碰到的问题。

multiple_inheritance.cpp:83:25: error: ‘Top’ is an ambiguous base of ‘Bottom’
     Top* t = new Bottom();

由这个问题,我去搜索资料。

  • 了解了什么

了解了c++ 在实现继承体系中的一些细节。

1.什么是虚函数表。

2.多重继承的二义性解决方法。虚继承。

虚函数表

A virtual method table (VMT), virtual function table, virtual call table, dispath table (分发表), vtable, or vftable is a mechanism used in a programming language to support dynamic dispatch(动态分发) (or run-time method binding) (运行时绑定即动态绑定).

详细内容可以查看下面的查看资料。里面讲的挺详细的。

我的理解。

因为在继承的时候,如果没有使用虚函数,即关键字virtual修饰函数。则用父类指针调用接口方法,则调用的是父类的方法。这是因为在类的虚函数表中(每个类都会有虚函数表,用于动态绑定),该指针指向的位置函数就是父类的方法。(函数其实就是一个二进制地址)。如果用了virtual修饰函数,则这时候用父类指针指向的接口方法,则是子类的方法。

set print object on//显示对象虚函数表
set print vtbl on//显示虚函数表//作用在我的机器上好像是一样的。

在上面的程序中,主函数是如下所示。

int main(){
    Top* pt = new Left();
    Left *pl = new Left();
    Left l;
    Fun pFun = NULL;
    pt->f();
    pt->Top::f();
    pFun();
    return 0;
}
(gdb) p *pt
$23 = (Left) {<Top> = {_vptr.Top = 0x400e30 <vtable for Left+16>, nData = 0}, <No data fields>}
(gdb) p *pl
$24 = (Left) {<Top> = {_vptr.Top = 0x400e30 <vtable for Left+16>, nData = 0}, <No data fields>}
(gdb) p l
$25 = (Left) {<Top> = {_vptr.Top = 0x400e30 <vtable for Left+16>, nData = 4196784}, <No data fields>}

打印p和pl和l所指向的内存。记得先打开显示对象的内容。

发现指针先指向的内容是对象的数据。

存放的内容是先存储虚地址表,然后存放数据段。

//顺着去打印gdb指向的虚函数表发现如下。
(gdb) p /a *(void**) 0x400e30@10//@10指向的内容是指打印10个。
$39 = {0x400ca6 <Left::~Left()>, 0x400ce0 <Left::~Left()>, 0x400d06 <Left::f()>, 0x400d30 <Left::g()>, 0x0, 0x0, 0x0, 0x400ec0 <_ZTI3Top>, 
  0x400bd8 <Top::~Top()>, 0x400c06 <Top::~Top()>}

可以发现,虚指针表指向的内容其实是函数入口。

而所有的入口函数都被换成了left的。我们现在把所有的virtual关键字去掉,看一下内存打印出来的函数入口是什么样子的。(这边有点奇怪,我gdb打印的pt和pl的地址居然会是一样的。)

(gdb) x /32a (void**) 0x400c00
0x400c00 <Top::~Top()+14>:	0xffbae8c78948f845	0x8948f8458b48ffff
0x400c10 <Top::~Top()+30>:	0xc3c9fffffceae8c7	0x10ec8348e5894855
0x400c20 <Top::f()+8>:	0x400d84bef87d8948	0x1de800602100bf00
0x400c30 <Top::f()+24>:	0x400970befffffd	0xfffffd20e8c78948
0x400c40 <Top::f()+40>:	0x8348e5894855c3c9	0x8b48f87d894810ec
0x400c50 <Left::Left()+14>:	0xff54e8c78948f845	0xc748f8458b48ffff
0x400c60 <Left::Left()+30>:	0x90c3c900400db000	0x10ec8348e5894855
0x400c70 <Left::~Left()+8>:	0xf8458b48f87d8948	0x4800400db000c748
0x400c80 <Left::~Left()+24>:	0x39e8c78948f8458b	0xb8ffffff
0x400c90 <Left::~Left()+40>:	0xf8458b480c74c085	0xfffffc60e8c78948
0x400ca0 <Left::~Left()+56>:	0x8348e5894855c3c9	0x8b48f87d894810ec
0x400cb0 <Left::~Left()+14>:	0xffaee8c78948f845	0x8948f8458b48ffff
0x400cc0 <Left::~Left()+30>:	0xc3c9fffffc3ae8c7	0x10ec8348e5894855
0x400cd0 <Left::f()+8>:	0x400d8bbef87d8948	0x6de800602100bf00
0x400ce0 <Left::f()+24>:	0x400970befffffc	0xfffffc70e8c78948

主要是想说明,在没有用虚函数的时候,内存存放的地址内容是先Top的所有方法,接着才是top的方法。

2.接下来讲一下虚继承

接着上面的实例

如果我想用基类作为指针指向其派生类Bottom。则需要在Left和right继承父类的时候实用virtual。

这样子编译的时候就可以通过了。

查看资料:

c++的多重继承要慎用: https://www.cnblogs.com/bourneli/archive/2011/12/28/2305264.html

C++虚函数表解析: http://blog.csdn.net/haoel/article/details/1948051

wiki上面的内容:https://en.wikipedia.org/wiki/Virtual_inheritance

RTTI、虚函数和虚基类的实现方式、开销分析及使用指导http://www.baiy.cn/doc/cpp/inside_rtti.htm

猜你喜欢

转载自my.oschina.net/u/2461850/blog/1603690