2

即使基类没有声明任何方法,特别是通过基类类型的对象调用“转换”方法,将派生类方法指针转换为基类方法的指针是否合法,如下

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

3 回答 3

5

值得注意的是,目标对象是逆变的原因是因为this实际上是传递给函数的参数,并且参数在理论上是逆变的(如果函数可以使用 a Base*,它可以安全地插入任何只提供Derived*作为实际参数)。

但是,对于任意参数,如果基子对象未放置在派生类布局的开头,则可能需要填充来调整指针。使用指向成员的指针,指针的指针调整this内置在语言中。(因此,指向具有虚拟继承的类成员的指针可能会变得很大)

于 2013-07-19T14:10:26.310 回答
2

不,这在标准的第 4.11 节中有描述(我有 n3337.pdf 草案):

“指向类型为 cv T 的 B 的成员的指针”类型的纯右值,其中 B 是类类型,可以转换为类型为“指向类型为 cv T 的 D 的成员的指针”类型的纯右值,其中 D 是派生类 ( B 的第 10 条)。如果 B 是 D 的不可访问(第 11 条)、模棱两可(10.2)或虚拟(10.1)基类,或 D 的虚拟基类的基类,则需要进行此转换的程序是格式不正确。转换的结果与发生转换之前的成员指针引用相同的成员,但它引用基类成员,就好像它是派生类的成员一样。结果引用了 D 的 B 实例中的成员。由于结果的类型为“指向类型为 cv T 的 D 成员的指针”,因此可以使用 D 对象取消引用。

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

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

虽然我相信您的代码冒了风险,但在某些时候,Borland 的 OWL 2.0 库中似乎大量使用了类似的转换(即使不涉及继承)。

于 2013-07-19T13:11:46.493 回答
0

强制转换指针是合法的。为了使用它,您必须将其转换回其原始类型。根本问题是指向函数的指针指向派生类的成员;不能保证该成员是基类的成员。

于 2013-07-20T13:10:00.490 回答