从基类向下转换时是否可以调用派生对象的虚拟方法?



给定如下类结构:

class Base
{
    virtual void outputMessage() { cout << "Base message!"; }
};
class Derived : public Base
{
    virtual void outputMessage() { cout << "Derived message!"; }
}

. .下面的代码片段:

Base baseObj;
Derived* convertedObj = (Derived*) &baseObj;
convertedObj->outputMessage();

. .输出将是"Base message!"。

是否有任何方法来强制转换或操作对象,以使派生的outputMessage方法的版本被多态调用?

编辑:我将尝试显示我在此之后的原因:

我正在编写连接到我们的主系统的迁移工具。出于这个原因,我需要访问受保护的成员方法,或者自定义现有的虚拟方法。前者可以通过定义派生类并向其强制转换对象来静态调用方法来实现。我不能做的是改变我不静态调用的方法的行为(即在代码库的其他地方调用的方法)。

我也尝试过直接创建派生类的对象,但这会导致系统其他部分的问题,因为通过构造函数传递对象的操作。

不,虚函数对所指向的对象的实际类型进行操作,在您的示例中,它只是一个简单的Base
实际上,随着向下转换,你进入了未定义行为的领域。这可能会像多重继承的炸弹一样爆炸,派生类中的虚值表与基类中的虚值表的偏移量不同。

没有符合标准的解决方案

你想做的是不可能使用c++标准保证的行为。

如果你真的必须这样做作为一个短期的措施来帮助你的迁移,不要在生产中依赖它,并且可以充分地验证行为,你可以实验如下所示。

讨论你的尝试

我要展示的是你采用了错误的方法:简单地将指向基的指针转换为指向派生的指针并不会改变对象的虚表指针。

得到一个合理的hack

解决这个问题,最简单的方法是就地重建对象作为派生对象("placement" new),但是这个也不起作用——它将重新初始化基类成员。

可以创建一个非派生对象,该对象没有数据成员,但具有相同的虚调度表项(即相同的虚函数,相同的访问权限private/protected/public,相同的顺序)。

更多警告和警告

可以工作(就像它在我的Linux机器上一样),但是使用它需要您自己承担风险(我建议在生产系统上使用而不是)。

进一步警告:这只能拦截虚分派,当编译器在编译时知道虚函数的类型时,有时可以静态地分派虚函数。

~/dev cat hack_vtable.cc
// change vtable of existing object to intercept virtual dispatch...
#include <iostream>
struct B
{
    virtual void f() { std::cout << "B::f()n"; }
    std::string s_;
};
struct D : B
{
    virtual void f() { std::cout << "D::f()n"; }
};
struct E
{
    virtual void f() { std::cout << "E::f()n"; }
};
int main()
{
    B* p = new B();
    p->s_ = "hello";
    new (p) D();  // WARNING: reconstructs B members
    p->f();
    std::cout << ''' << p->s_ << "'n"; // no longer "hello"
    p->s_ = "world";
    new (p) E();
    p->f();  // correctly calls E::f()
    std::cout << ''' << p->s_ << "'n"; // still "world"
}
~/dev try hack_vtable   
make: `hack_vtable' is up to date.
D::f()
''
E::f()
'world'

嗯,即使您将Base对象转换为派生对象,在内部,它仍然是一个Base对象:对象的vftable(函数到RAM指针的实际映射)不会更新。我不认为有任何方法可以做你想做的事,我不明白你为什么要这样做。

在这个问题中c++的问题rob的答案也应该是你的问题的答案。

至少在法律上没有。要调用派生类函数,需要引用派生对象。

最新更新