剧透警报:也许是一个愚蠢的问题。 :)
#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
现在,我知道这段代码无法运行。我想知道为什么。 Base
对Intermediate
是私有的,因此Derived
到Intermediate
不应显示{{1}}。那么模糊性来自何处?在构造函数中?
答案 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。,