2013-07-19 132 views
2

指针指向派生类指向指针指向基类的方法是合法的,即使基类没有声明任何方法,特别是的“铸造”的方法是通过型基类的对象被调用,如follows将指向派生类的方法的指针投射到指向基类的方法的指针

// works in VS 2008 and g++ 4.5.3 
struct Base 
{ 
}; 

struct Fuu : public Base 
{ 
    void bar(){ std::cout << "Fuu::bar" << std::endl; } 
    void bax(){ std::cout << "Fuu::bax" << std::endl; } 
}; 

struct Foo : public Base 
{ 
    void bar(){ std::cout << "Foo::bar" << std::endl; } 
    void bax(){ std::cout << "Foo::bax" << std::endl; } 
}; 

typedef void (Base::*PtrToMethod)(); 

int main() 
{ 
    PtrToMethod ptr1 = (PtrToMethod) &Foo::bax; 
    PtrToMethod ptr2 = (PtrToMethod) &Fuu::bax; 

    Base *f1 = new Foo; 
    Base *f2 = new Fuu; 

    (f1->*ptr1)(); 
    (f2->*ptr2)(); 
} 
+1

对于“普通”指针,该规则被称为“协方差”:您可以将“派生*”转换为“基本*”(因为“派生”是“基本”),而不是相反方向(因为“ Base *'不一定指向'Derived')。对于_pointers-to-member_,这个规则被称为“contravariance”,并且基本上是与之相反的:您可以将'T Base :: *'转换为'T Derived :: *'(因为'Base'的成员也是存在于一个Derived'对象中)而不是其他方式(因为'Derived'可以将成员添加到'Base'中)。另外_please_不使用C风格演员:他们不安全,不清楚,难以发现。 –

+0

精细打印:您的示例虽然编译和“有效”,但可能不合法。或者将'Base *''f1'强制转换为'Foo *'(如果你确定的话''static_cast'',如果你不需要''dynamic_cast''但是它需要'Base'至少有一个'virtual'函数] ),或者添加虚拟功能到'Base'的公共接口。 –

回答

5

值得注意的是,目标对象是逆变的原因是因为this实际上是一个传递给函数的参数,理论上,参数是逆变的(如果函数可以使用Base*,它可以安全地插入转换成仅提供Derived*作为实际参数的任何算法)。

但是,对于任意参数,如果基础子对象未放置在派生类布局的开始位置,则可能需要使用填充来调整指针。使用指向成员的指针,this指针的指针调整内置在语言中。 (由于这个原因,指向虚拟继承类的成员可能变得非常大)

+0

我总是用托尼女高音的格言:钱涨了,狗屎跑下坡了。在这种情况下:向下转换参数很麻烦,向上转换是很自然的。 – TemplateRex

+0

我理解成员函数的“暗示'这个'参数”推理(“问题”提到的“方法”),但为了完整性,它能否扩展到成员_variables_? –

+2

@gx_:从理论上看,指向成员变量的指针*是一个函数,它将强类型对象地址映射到强类型子对象地址。在存在虚拟继承的情况下,这些函数被限定为方法,因为它们是动态分派的。在大多数实现中,从不使用实际函数调用,子对象访问代码直接插入到使用位置,但这与内联任何其他函数没有区别。 –

2

这是在标准的第4.11(描述我有n3337.pdf草案):

“类型cv T的B的成员的指针”的类型的值,其中B是 类类型,可以被转换为类型为“指向类型cv T的D的成员 ”的类型的值,其中D是B的派生类(条款10)。如果B 是D的基类或D的虚基类的基类,则不可访问(条款11),含糊(10.2)或虚拟(10.1) 需要此转换的程序不合格。 的结果转换指的是在转换发生之前指向成员 的指针相同的成员,但它指的是基类 成员,就好像它是派生类的成员一样。结果在D的实例B中将成员 引用到成员。由于结果具有类型“指针 到类型cv T的成员D”,因此可以用D对象取消引用。 结果是一样的,如果指针B的构件被解除引用与D的乙子对象空构件指针值 被转换为目的地 type.57

的空构件指针值

通常,指向成员转换的指​​针在相反方向工作,然后指向派生/基类。指向(子)对象的指针可以转换为基类,指向方法的指针可以转换为更多的派生类。

请注意,如果您涉及的对象不是Foo/Fuu类型或其派生类型时尝试通过上述任何指针进行调用,则您的程序会生病。

虽然我相信你的代码冒着风险,它看起来像类似的转换(即使没有涉及继承),在某些时候,它在Borland的OWL 2.0库中被大量使用。

0

转换指针是合法的。为了使用它,你必须把它转换回原来的类型。根本问题是指向函数的指针指向派生类的成员;无法保证该成员是基类的成员。

相关问题