#include<iostream>
using namespace std;
class A
{
int a;
int b;
public:
void eat()
{
cout<<"A::eat()"<<endl;
}
};
class B: public A
{
public:
void eat()
{
cout<<"B::eat()"<<endl;
}
};
class C: public A
{
public:
void eat()
{
cout<<"C::eat()"<<endl;
}
};
class D: public B, C
{
};
int foo(A *ptr)
{
ptr->eat();
}
main()
{
D obj;
foo(&(obj.B)); //error. How do i call with D's B part.
}
上面的foo调用是编译时错误。 我想用obj的B部分调用foo而不使用虚拟继承。我怎么做。
此外,在虚拟继承的情况下,为什么需要将偏移信息存储在vtable中。这可以在编译时自己确定。在上面的例子中,如果我们用D的对象传递foo,在编译时我们只能计算D的A部分的偏移量。
答案 0 :(得分:6)
使用双重继承你有一个歧义 - 编译器无法知道你想要使用哪两个A基。如果你想拥有两个A基(有时你可能想要这样做),你可以通过转换为B或C来选择它们。这里默认转换最合适的是static_cast
(最弱的可用) ,但它并不是真正需要的(它仍然比你的情况需要更强),因为你没有强制转换为派生类型。自定义safe_cast
模板应该可以胜任:
/// cast using implicit conversions only
template <class To,class From>
inline To safe_cast( const From &from ) {return from;}
main()
{
D obj;
foo(safe_cast<B *>(&obj)); //error. How do i call with D's B part.
}
另外,在虚拟继承的情况下, 为什么偏移信息需要 存储在vtable中。这可以 在编译时确定。 在上面的例子中,如果我们传递foo D的对象,只在编译时我们 可以计算D'的偏移量 一部分。
这是一种误解。现在编写的foo函数没有关于ptr类型的编译类型信息,除非它是A *,即使你传递B *或C *。如果您希望foo能够根据传递编译时间的类型进行操作,则需要使用模板:
template <class TypeDerivedFromA>
int foo(TypeDerivedFromA *ptr)
{
ptr->eat();
}
您的问题提到了虚拟继承。如果要使用虚拟继承,则需要指定:
class B: public virtual A ...
class C: public virtual A ...
这样代码就可以编译了,但是使用这个解决方案,你无法在B :: A或C :: A(只有一个A)之间进行选择,因此这可能与你无关。 / p>
此外,您的问题似乎混淆了两个不同的概念,虚拟继承(这意味着在两个中间基类之间共享一个基类)和虚函数(这意味着允许通过基类指针调用派生类函数)。如果你想使用A指针调用B :: eat,你可以使用虚函数执行此操作而不使用虚拟继承(实际上虚拟继承会阻止你这样做,如上所述):
class A
{
int a;
int b;
public:
virtual void eat()
{
cout<<"A::eat()"<<endl;
}
};
如果你不接受虚函数,那么编译时机制就是模板,如上所述。
答案 1 :(得分:3)
使用强制转换 - 此处需要static_cast
来强制转换层次结构。
main()
{
D obj;
foo(static_cast<B*>(&obj));
}
答案 2 :(得分:1)
首先,obj
没有名为B的成员。它继承自B,这意味着它继承了B的所有成员。
您可以致电:
foo(static_cast<B*>(&obj));
让它发挥作用。
答案 3 :(得分:1)
我不认为static_cast会起作用。
当你使用foo函数时,所有编译器都知道你有一个指向A的指针,无论你传递给参数的类型是什么。
如果你不使用虚拟继承,那么我认为没有办法从指向A的指针调用B函数。