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

nak*_*iya 19 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()
{
}
Run Code Online (Sandbox Code Playgroud)

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

main.cpp:21: warning: direct base ‘Base’ inaccessible in ‘Derived’ due to ambiguity
Run Code Online (Sandbox Code Playgroud)

现在,我知道这段代码无法运行.我想知道为什么.Base是私有的Intermediate所以它不应该是可见的Derived通过Intermediate.那么模糊性来自何处?在构造函数中?

Joh*_*itb 28

这与重写函数无关.它与转换有关.它实际上与可访问性(即"私有"或类​​似)无直接关系.这是一个更简单的例子

struct A { int a; };
struct B : A { };
struct C : B, A { }; // direct A can't be referred to!
Run Code Online (Sandbox Code Playgroud)

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

B *b = &somec;
A *a = b;
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • @nakiya 从技术上讲这可能是有道理的(我认为在 Java 中,私有成员名称不是继承的,与 C++ 相反),但 C++ 规则并非如此:可访问性对任何事情都无关紧要 - 只有在其他所有内容都存在时才检查它已确立的。我不知道这样做的基本原理 - 也许是出于简单的原因(朋友的姓名查找可能与非朋友的姓名查找完全不同)。 (2认同)

Che*_*Alf 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.
}
Run Code Online (Sandbox Code Playgroud)

当我编译时,我得到了,就像现在(在约翰内斯回答之后)你会期待的那样,

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.

喜欢:

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.
}
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,例如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.
}
Run Code Online (Sandbox Code Playgroud)

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

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

干杯&hth.,