这个问题是关于使用不同的返回类型覆盖派生类中的虚方法。 对于以下代码:
class father {
public:
virtual father* ref() { return this; }
};
class child : public father {
virtual child* ref() { return this; }
};
当我尝试直接获取指针时,g ++(F15,g ++ 4.5)报告“从父亲到孩子的无效转换”
child m_child;
father* pf = &m_child;
child* pc = pf->ref();
我理解使用子类中的ref()方法,这可能只是编译时类型不匹配。
但是,如果没有明确使用类型转换,有没有办法呢?
额外说明: 我理解编译器报告此错误的原因。我需要的是开箱即用的东西,它可以访问派生对象中的数据而无需显式转换指针。
父类用于将不同的派生子对象放入列表或向量中,因此当从列表或向量中获取项时,无法分辨它属于哪个子类。
我有内部方法来记录和检查子类类型。但我不想显式转换指针。
例如,我想做这样的事情:
// assuming pf is pointer pointed to an item fetch from a vector
switch(fp->get_type()) {
case child_type1: fp->ref()->list1.push(data); break;
case child_type2: fp->ref()->list2.push(data); break;
case child_type3: fp->ref()->list3.push(data); break;
}
现在,我需要显式声明一个新变量,或者在每种情况下显式地将fp转换为正确的类型,每次我需要访问派生类中的数据时,这些都很乏味且令人困惑。
我期望的是:可能是某些boost库可以用另一种我不知道的方式做类似的事情,或者可能是c ++ 11标准允许但是需要设置一个特殊的编译参数?
答案 0 :(得分:2)
简单回答是“不”。
当您丢弃child*
类型信息时,通过将其存储为指向基座的指针,您丢失了关于father*
的额外信息(ref
而不是m_child
) (pf
)。
这个例子就是没有演员阵容永远不可能的一个原因:
class father {
public:
virtual father* ref() { return this; }
};
class childA : public father {
virtual childA* ref() { return this; }
};
class childB : public father {
virtual childB* ref() { return this; }
};
void should_never_compile(int i)
{
childA a;
childB b;
father pf;
if( i ) { pf=&a; }
else { pf=&b; }
// This is evil and will not compile
childA * pa = pf->ref();
//But this is OK:
childA * pa = dynamic_cast<childA*>(pf->ref() );
}
如果你真的想在没有动态演员阵容的情况下做到这一点,你可以隐藏动态演员阵容(但这让我有点害怕)
class father {
public:
virtual father* ref() { return this; }
template<typename T> T* as() { return dynamic_cast<T*>(ref()); }
};
child m_child;
father* pf = &m_child;
child* pc = pf->as<child>();
答案 1 :(得分:1)
您的功能符合您的期望以及您告诉它的内容。这条线
child* pc = pf->ref();
是问题所在。你在父指针上调用函数“ref()”,它返回(按类型)父亲*。您将此 - 无需转换 - 分配给孩子*。该函数的实现返回一个子*,但从你称之为的信息不知道,因为你只知道它是父亲*。因此返回类型是父*(如类父亲所示),并且您可以在不转换为子*的情况下分配它。你可以做:
child *pc = m_child.ref();
father *pf = m_child.ref();
father *pf2 = pf->ref();
答案 2 :(得分:1)
问题是,在通话pf->ref()
中,ref
的类型为:father* (father::*)()
。
也就是说,ref
已解析静态为具有给定签名的virtual
方法。此签名表示它返回father*
。
因此:
int main() {
father f;
father* pf = &f;
child c;
child* pc = &c;
child* xf = pf->ref(); // compile-time failure
child* xc = pc->ref(); // okay
}
问题在于,一般情况下,从father*
开始,您无法知道是否会获得child*
,这取决于实例的动态类型。因此,编译器假设最坏的情况:它必须至少为father
。