虚拟类的多重继承

时间:2011-07-28 15:32:44

标签: c++ oop virtual multiple-inheritance

假设我有以下代码:

class a {
public:
    virtual void do_a() = 0;
}

class b {
public:
    virtual void do_b() = 0;
}

class c: public a, public b {
public:
    virtual void do_a() {};
    virtual void do_b() {};
}

a *foo = new c();
b *bar = new c();

foo->do_a()bar->do_b()会有效吗?这里的内存布局是什么?

7 个答案:

答案 0 :(得分:4)

  

a-> do_a()和b-> do_b()会起作用吗?

假设您的意思是foo->do_a()bar->do_b(),因为ab不是对象,它们是类型,是的。他们会工作的。你尝试过那个吗?

  

这里的内存布局是什么?

这主要是实现定义的。幸运的是,除非你想编写不可移植的代码,否则你不需要知道它。

答案 1 :(得分:3)

他们为什么不应该?内存布局通常类似于:

+----------+
|  A part  |
+----------+
|  B part  |
+----------+
|  C part  |
+----------+

如果您将foobar转换为void*并展示它们,那么您将会 得到不同的地址,但编译器知道这一点,并会安排 调用时,this指针可以正确修复 功能。

答案 2 :(得分:2)

正如其他人所说,以下内容可以正常运行

foo->do_a();
bar->do_b();

然而,这些不会编译

bar->do_a();
foo->do_b();

由于bar的类型为b*,因此它不了解do_afoodo_b也是如此。如果你想进行那些函数调用,你必须保持低调。

static_cast<c *>(foo)->do_b();
static_cast<c *>(bar)->do_a();

在示例代码中未显示的另一个非常重要的事物是,在继承时,通过基类指针引用派生类时,基类必须有一个虚拟析构函数。如果没有,则以下内容将产生未定义的行为。

a* foo = new c();
delete a;

修复很简单

class a {
public:
    virtual void do_a() = 0;

    virtual ~a() {}
};

当然,此更改也需要b

答案 3 :(得分:1)

foo->do_a();   // will work
bar->do_b();   // will work
bar->do_a();   // compile error (do_a() is not a member of B)
foo->do_b();   // compile error (do_b() is not a member of A)

// If you really know the types are correct:
C* c = static_cast<C*>(foo);

c->do_a();  // will work
c->do_b();  // will work

// If you don't know the types, you can try at runtime:
if(C* c = dynamic_cast<C*>(foo))
{
    c->do_a();  // will work
    c->do_b();  // will work
}

答案 4 :(得分:1)

是的,当然会起作用。虽然机制有点棘手。该对象将有两个vtable,一个用于类父类,一个用于类b父类。将调整指针,使它们指向对应于指针类型的对象子集,从而产生这种令人惊讶的结果:

c * baz = new c;
a * foo = baz;
b * bar = baz;
assert((void *)foo == (void *)bar); // assertion fails!

编译器知道赋值时的类型,并确切知道如何调整指针。

这当然完全取决于编译器; C ++标准中没有任何内容表明它必须以这种方式工作。只有它必须工作。

答案 5 :(得分:0)

  

a-&gt; do_a()和b-&gt; do_b()会起作用吗?

没有

foo->do_a()bar->do_b()会有效吗?

是。您的代码是虚函数调度的典型示例。

你为什么不试试呢?

  

这里的内存布局是什么?

谁在乎?

(即这是实现定义的,并从您身上抽象出来。您不应该也不想知道。)

答案 6 :(得分:0)

他们会工作。就内存而言,这取决于实现。您已经在堆上创建了对象,对于大多数系统,值得注意的是堆上的对象会向上增长(例如,堆栈向下增长)。所以可能,你将拥有:

Memory:

+foo+
-----
+bar+