给出以下类结构:
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!”。
有没有办法转换或操作对象以使Derived的outputMessage方法版本被多态调用?
修改:我会尝试说明我之后的原因:
我正在编写挂钩到我们主系统的迁移工具。出于这个原因,我需要访问受保护的成员方法,或自定义现有的虚拟方法。前者我可以通过定义派生类并将对象强制转换为静态来调用方法。我不能做的是改变我不静态调用的方法的行为(即在代码库中的其他地方调用的方法)。
我还尝试直接创建派生类的对象,但由于操作通过构造函数传递的对象,这会导致系统其他部分出现问题。
答案 0 :(得分:5)
不,虚函数对指向的对象的实际类型进行操作,在您的情况下只是一个简单的Base
。
实际上,通过降级,你在这里进入未定义行为的土地。这可以像一个具有多重继承的炸弹一样被吹掉,其中派生类中的vtable与基类中的vtable的偏移量不同。
答案 1 :(得分:1)
好吧,即使您将Base对象作为派生对象进行内部渲染,它仍然是一个Base对象:对象的vftable(RAM指针的实际函数映射)不会更新。 我认为没有办法做你想做的事情,我不明白你为什么要这样做。
答案 2 :(得分:1)
在这个问题中downcast problem in c++ Robs的回答也应该是你问题的答案。
答案 3 :(得分:1)
您尝试做什么无法使用C ++标准保证的行为。
如果您真的必须将此作为短期措施来帮助您迁移,不要在生产中依赖它,并且可以充分验证行为,您可以实验,如下图所示。
我所展示的是你采取了错误的方法:简单地将指针指向某个指向派生的指针不会改变对象的vtable指针。< / p>
解决这个问题,天真的方法是将对象重建为派生对象(“placement”new
),但不起作用 - 它将重新初始化基类成员。
可能做的是创建一个非派生对象,该对象没有数据成员但相同的虚拟调度表条目(即相同的虚函数,相同的辅助功能私有) / 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'
答案 4 :(得分:0)
至少不合法。要调用Derived类函数,您需要引用Derived对象。