【问题标题】:Is it safe to "upcast" a method pointer and use it with base class pointer?“向上转换”方法指针并将其与基类指针一起使用是否安全?
【发布时间】:2011-05-15 10:44:04
【问题描述】:

假设我有一个指针类型,它可以保存基类方法的地址。我可以将子类方法的地址分配给它并期望它正常工作吗?就我而言,我将它与基类指针一起使用,对象的动态类型是派生类。

struct B
{
    typedef void (B::*MethodPtr)();
};

struct D: public B
{
    void foo() { cout<<"foo"<<endl; }
};

int main(int argc, char* argv[])
{
    D d;
    B* pb = &d;

    //is the following ok, or undefined behavior?
    B::MethodPtr mp = static_cast<B::MethodPtr>(&D::foo);
    (pb->*mp)();
}

标准在谈到 static_cast 时这样说:

5.2.9.9 “指向 cv1 T 类型的 D 成员的指针”类型的右值可以转换为“指向 cv2 T 类型的 B 成员的指针”类型的右值,其中 B是 D 的基类(第 10 条),如果存在从“指向 T 类型 B 成员的指针”到“指向 T 类型 D 成员的指针”的有效标准转换(4.11),并且 cv2 相同 cv-qualification 与 cv1 相同或更高的 cv-qualification。 63)将空成员指针值(4.11)转换为目标类型的空成员指针值。如果类 B 包含原始成员,或者是包含原始成员的类的基类或派生类,则指向成员的结果指针指向原始成员。否则,强制转换的结果是未定义的。 [注:虽然B类需要 不包含原始成员,取消引用指向成员的指针的对象的动态类型必须包含原始成员;见 5.5.]

与往常一样,我很难破译标准。它有点说没关系,但我不能 100% 确定上述文本是否真的适用于我的示例代码中的情况。

【问题讨论】:

  • 为什么不能使用常规的虚函数覆盖?
  • 编码有点像走在山路上,走在路中间比走近边缘更安全,你的代码看起来就近边缘了。 :-)

标签: c++ polymorphism member-function-pointers


【解决方案1】:

有效。

如果类 B 包含原始成员,

B 不包含 D::Foo,所以没有。

or 是包含原始成员的类的基 [...]

B 是 D 的底,所以这成立。结果:

指向成员的结果指针指向原始成员

第 5.2.9 9 条规定,只有当您也可以向下转换时,您才能向上转换,如 § 4.11 中所述:

“指向类型为 cv T 的 B 的成员的指针”类型的右值,其中 B 是类类型,可以转换为类型为“指向类型为 cv T 的 D 的成员的指针”类型的右值,其中 D 是B 的派生类(第 10 条)。如果 B 是 D 的不可访问(第 11 条)、模棱两可 (10.2) 或虚拟 (10.1) 基类,则需要这种转换的程序是不正确的。

这只是说只要 B 是可访问的,不是虚拟的并且只在 D 的继承图中出现一次,就可以向下转换。

向上转换方法指针的内在危险在于,您可以在实际类型为 B 的对象上调用 mp。只要处理 D::* 的代码块也处理 D*,就可以避免这种情况.

【讨论】:

  • +1,但我认为最后一句话有点误导,因为拥有D* 并不能保证指向对象的动态类型实际上是D(或从@987654324 派生的某种类型@),这是你真正需要的属性。
  • 是的,我对措辞不太满意,但我很难想出一个足够广泛、正确和简洁的规则。也许写“介绍”而不是“交易”?
  • As long as a code block that deals with D::* also deals with a D* which points to an object whose dynamic type is either D or derived from D 怎么样?不过,可能过于冗长。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多