覆盖虚方法时,无法返回派生类指针

时间:2012-04-04 13:15:35

标签: c++ g++

这个问题是关于使用不同的返回类型覆盖派生类中的虚方法。 对于以下代码:

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标准允许但是需要设置一个特殊的编译参数?

3 个答案:

答案 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