2015-07-20 94 views
5

手头的问题很难描述,所以代码被放在前面以获得更好的清晰度。多继承虚拟呼叫模糊

struct Base 
{ 
    int b; 
    virtual void foo(){cout << b << endl;} 
    Base(int x) : b(x){} 
}; 

struct Derived1 : Base //not virtual 
{ 
    virtual void foo(){/*Derived2's code*/} 
    Derived1() : Base(1){} 
}; 

struct Derived2 : Base //not virtual 
{ 
    virtual void foo(){/*Derived2's code*/} 
    Derived2() : Base(2){} 
}; 

struct MultiInheritance : Derived1, Derived2 
{ 
    void bar1() 
    { 
     //needs to access Derived1's Base foo() 
    } 
    void bar2() 
    { 
     //needs to access Derived2's Base foo() 
    } 
}; 

假设在有些怪异离奇的情况下,我想一个基类MultiInheritance有两个基类Derived1Derived2有一个共同的非虚基类Base

有两个BaseMultiInheritance,我怎么指定我想在MultiInheritance访问哪些Base类?

上面的代码似乎很好地通过投射多次,但我不知道这是否定义的行为。如果是这样,编译器如何实现它以满足多态性的需求?一方面virtual调用应该都产生相同的virtual函数表,但另一方面,如果它不会输出不同的答案。

编辑

我想强调的是,Base类必须是非虚拟

EDIT2

深深的歉意,我严重歪曲了自己。上面的代码更新更好地反映了我原来的问题。

+0

哪一个你认为的正确* *'Base'?或者是你的问题如何使它只有一个'Base'? – Barry

+0

你的分析是正确的,编译器会建立一个反映两个基类存在的正确的vtable。为了避免这种情况,您需要虚拟继承,并且vtable结构将变得更加复杂。 –

+1

您的示例中没有任何歧义,它的行为与书中的完全相同。 “虚拟表”是一个实现细节。没有人在任何地方说每个实现必须每个类有一个vtbl,或者每个vtable中的每个函数签名都必须有一个条目。实际上,对于使用vtables的任何实现来说,上述至少一个是不正确的。 –

回答

0

以下是一个说明性的例子。

#include <iostream> 
using namespace std; 

template <typename Res, typename Arg> 
Res& as(Arg& arg) 
{ 
    return arg; 
} 

struct Base 
{ 
    virtual void foo() = 0; 
}; 

struct Derived1 : Base {}; 
struct Derived2 : Base {}; 

struct MoreDerived1 : Derived1 
{ 
    void foo() { cout << "Derived1\n"; } 
}; 

struct MoreDerived2 : Derived2 
{ 
    void foo() { cout << "Derived2\n"; } 
}; 

struct MultiInheritance : MoreDerived1, MoreDerived2 
{ 
    void bar1() { as<Derived1>(*this).foo(); } 
    void bar2() { as<Derived2>(*this).foo(); } 
}; 

int main() 
{ 
    MultiInheritance m; 
    m.bar1(); 
    m.bar2(); 
} 

这个例子说明:

  1. 你并不需要指定其基础则需要明确使用一个完整的继承路径,这就够了,深入到有一个明确的基地的子对象子对象
  2. 虚拟功能机制在此处起作用。如果您尝试致电Derived1::foo(),则不起作用。

as辅助函数只是一个语法糖,你也可以同样说

Derived1& d = *this; 
d.foo(); 
2

这被称为钻石问题。

http://www.cprogramming.com/tutorial/virtual_inheritance.html

如果你想保底非虚,让你现在正在寻找的行为,你可以做到这里面MultipleInheritance以下方式以确保您从正确的基类

调用 foo()功能
struct MultiInheritance : Derived1, Derived2 
{ 
    void bar1() 
    { 
     Derived1::foo(); 
    } 
    void bar2() 
    { 
     Derived2::foo(); 
    } 
}; 
0

有两个基地MultiInheritance,我怎么指定基地 类我希望在MultiInheritance访问?

你必须在基础对象,你在呼唤在

void MultiInheritance::bar1(){ 
    foo(); 
} 

的方式来解决,这是告诉编译器到哪里寻找FOO歧义。

void MultiInheritance::bar1(){ 
    Derived1::foo(); // The same foo() as in your question. 
} 

这就是你的

void MultiInheritance::bar1() 
{ 
    Derived1& d = *this; 

这是由§10.2.12标准中所述完成。这是明确的。至于你的连锁

void MultiInheritance::bar1() 
{ 
    Derived1& d = *this; 
    Base& b = *this; 

在同一段落。

不幸的是范围解析运营商不能让你从MultiInhteritance直接跳转到基地

MultiInheritance::foo(){ 
    Derived1::Base::foo(); 

被描述嵌套Base

要到把foo()属于基地使用范围解析语法MultiInheritance,以及在Derived1和Derived2的。

Derived1()::foo(){ 
    Base::foo; 

如果这不合适,那么您建议的选项是剩余选项。

如果是这样,编译器如何实现多态的需求 ?一方面虚拟通话应该都会导致 同样的虚拟功能表,但另一方面,如果它不会, 输出不同的答案。

编译器实现因编译器而异,正如一位评论者所说:虚拟函数使用vtables是一个实现细节。如果一个实现使用虚拟功能的虚拟表,实施需要考虑到这种情况。