由多重继承引起的“无法访问的直接基础”

时间:2010-11-07 15:40:26

标签: c++ multiple-inheritance

剧透警报:也许是一个愚蠢的问题。 :)

#include <iostream>

using namespace std;

class Base
{
    public:
        virtual void YourMethod(int) const = 0;
};

class Intermediate : private Base
{
    public:
        virtual void YourMethod(int i) const
        {
            cout << "Calling from Intermediate" << i << "\n";
        }
};

class Derived : private Intermediate, public Base
{
    public:
        void YourMethod(int i) const
        {
            cout << "Calling from Derived : " << i << "\n";
        }
};

int main()
{
}

有人可以向我解释为什么会抛出编译器警告

main.cpp:21: warning: direct base ‘Base’ inaccessible in ‘Derived’ due to ambiguity

现在,我知道这段代码无法运行。我想知道为什么。 BaseIntermediate是私有的,因此DerivedIntermediate不应显示{{1}}。那么模糊性来自何处?在构造函数中?

2 个答案:

答案 0 :(得分:26)

这与覆盖功能无关。它与转换有关。它实际上与可访问性(即“私有”或类​​似)无直接关系。这是一个更简单的例子

struct A { int a; };
struct B : A { };
struct C : B, A { }; // direct A can't be referred to!

您可以先转换为A然后转换为B来引用间接A对象:

B *b = &somec;
A *a = b;

您无法使用直接A对象执行此操作。如果您尝试直接转换为A,则会有两种可能性。因此,在给定A对象的情况下,不可能引用直接Derived对象的非静态数据成员。

请注意,辅助功能与可见性正交。即使它不可见,也可以访问某些东西(例如通过合格的名称引用它),即使它不可访问,也可以看到某些东西。即使所有上述派生都被声明为private,问题仍然会出现:最后检查访问 - 它不会影响名称查找或转换规则。

此外,任何人都可以使用C风格的强制类型转换为具有已定义行为的明确私有基类(C ++标准为此做出异常),即使通常不会授予访问权限。然后还有朋友和班级本身可以自由转换。

答案 1 :(得分:7)

约翰内斯的回答涵盖了基本事实。但还有一点。所以,考虑一下

struct Base
{
    Base( int ) {}
    void foo() const {}
};

struct Intermediate: Base
{
    Intermediate( int x )
        : Base( x )
    {}
};

struct Derived: Intermediate, Base
{
    Derived( int x )
        : Intermediate( x )
        , Base( x )         // OK
    {}
};

int main()
{
    Derived o( 667 );
    o.foo();                // !Oops, ambiguous.
    o.Base::foo();          // !Oops, still ambiguous.
}

当我编译时,我得到了,就像现在(在Johannes的回答之后),你会期待的,

C:\test> gnuc x.cpp
x.cpp:15: warning: direct base 'Base' inaccessible in 'Derived' due to ambiguity
x.cpp: In function 'int main()':
x.cpp:25: error: request for member 'foo' is ambiguous
x.cpp:4: error: candidates are: void Base::foo() const
x.cpp:4: error:                 void Base::foo() const
x.cpp:26: error: 'Base' is an ambiguous base of 'Derived'

C:\test> msvc x.cpp
x.cpp
x.cpp(15) : warning C4584: 'Derived' : base-class 'Base' is already a base-class of 'Intermediate'
        x.cpp(2) : see declaration of 'Base'
        x.cpp(7) : see declaration of 'Intermediate'
x.cpp(25) : error C2385: ambiguous access of 'foo'
        could be the 'foo' in base 'Base'
        or could be the 'foo' in base 'Base'
x.cpp(25) : error C3861: 'foo': identifier not found

C:\test> _

如何解决取决于是否可以使用类Base的单个子对象(如Base是纯接口时的情况),或者Intermediate确实需要它自己的Base子对象。

后一种情况,两个Base子对象,可能不是你想要的,但如果你想要那么,那么一种治疗就是引入另一个中间类,比如ResolvableBase。 / p>

像:

struct Base
{
    Base( int ) {}
    void foo() const {}
};

struct Intermediate: Base
{
    Intermediate( int x )
        : Base( x )
    {}
};

struct ResolvableBase: Base
{
    ResolvableBase( int x ): Base( x ) {}
};

struct Derived: Intermediate, ResolvableBase
{
    Derived( int x )
        : Intermediate( x )
        , ResolvableBase( x )
    {}
};

int main()
{
    Derived o( 667 );
    o.ResolvableBase::foo();    // OK.
}

在第一种情况下,例如Base是一个接口,只需要一个Base子对象,您可以使用虚拟继承。

虚拟继承通常会增加一些运行时开销,而Visual C ++并不太喜欢它。

但是它允许你“继承”接口的实现,比如Java和C#:

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

struct Intermediate: virtual Base
{
    Intermediate( int x )
        : Base( x )
    {}
    void foo() const {}     // An implementation of Base::foo
};

struct Derived: virtual Base, Intermediate
{
    Derived( int x )
        : Base( x )
        , Intermediate( x )
    {}
};

int main()
{
    Derived o( 667 );
    o.foo();    // OK.
}

微妙:我更改了继承列表顺序,以避免g ++关于初始化顺序的愚蠢警告。

烦恼:Visual C ++通过支配问题发布关于继承(实现)的愚蠢的C4250问题。这就像“警告:你正在使用标准的主要功能”。哦,好吧,把它关掉。

干杯&amp;第h。,