代码之家  ›  专栏  ›  技术社区  ›  Timo

“上溯”方法指针并将其与基类指针一起使用是否安全?

  •  11
  • Timo  · 技术社区  · 14 年前

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

    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)();
    }
    

    当谈到静态广播时,标准是这样说的:

    5.2.9.9节 CV1 T类型D成员D的类型指针的R值可以转换为CV2 T类型B的类型指针的值,其中B是D的基类(子句10),如果从T类型的指针到T类型D的成员的指针的有效标准转换存在(4.11),并且CV2是相同的。 cv认证为或大于cv1。63)空成员指针值(4.11)转换为目标类型的空成员指针值。如果类B包含原始成员,或者是包含原始成员的类的基类或派生类,则指向成员的结果指针指向原始成员。否则,转换的结果是未定义的。[注:虽然B类需要 不包含原始成员,取消引用成员指针的对象的动态类型必须包含原始成员;请参见5.5。]

    像往常一样,我很难破译标准。它有点说它是可以的,但是我不确定上面的文本是否真的适用于我的示例代码中的情况。

    1 回复  |  直到 14 年前
        1
  •  9
  •   outis    14 年前

    它是有效的。

    如果B类包含原始成员,

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

    或者是包含原始成员的类的基[…]

    B是D的基,所以这个成立。因此:

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

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

    指向cv T类型B的成员的rvalue类型指针,其中B是类类型,可以转换为指向cvt类型D的成员的rvalue类型指针,其中D是B的派生类(第10条)。如果B是D的不可访问(第11条)、不明确(10.2)或虚拟(10.1)基类,则需要此转换的程序格式错误。

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

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