我试图编写一些代码,我可以在堆栈上创建基类,并通过调用该基类来修改vtable。
class Base
{
public:
void initAs(int version);
virtual int foo() { assert(false); return 0; }
};
class A : public Base
{
public:
virtual int foo() { return 1; }
};
void Base::initAs(int version)
{
switch(version)
{
case 1:
new (this) A();
break;
default:
break;
}
}
int main()
{
Base x;
int version = 1;
x.initAs(version);
int v = x.foo();
assert(v == 1);
return 0;
}
我遇到了内联x.foo()以调用Base :: foo()而不是A :: foo()的问题。我检查了反汇编,没有vtable分辨率。编译器决定内联该虚函数,即使它应该能够看到指向x的内存正在被修改。如何阻止编译器(MSVC 14)内联函数调用x.foo()?
编辑: 我不是在寻找关于未定义此行为的评论或答案。据我所知,我应该期望x.foo()应该遍历整个虚函数调用堆栈,因为它被标记为虚函数,我无处可去完全限定函数名称(akaxBase :: foo( ))。我在代码中有其他地方可以工作:
class Container
{
private:
Base x;
Container();
void foo();
};
Container::Container() { x.initAs(1); }
void Container::foo() { assert(x.foo() == 1; } // This call is correct A::foo()
编译器中的差异在哪里?如何将其关闭?
答案 0 :(得分:2)
我无法发表评论,我自己也没有尝试过这些内容,但我想做出以下观察 - 你可能无法强迫编译器不内联函数。编译器在内联方面是出了名的不一致,即使inline
关键字没有强制它内联(它更像是一个提示,它也使得该函数对其他编译单元不可见),并且没有inline
关键字并不意味着它无法内联。这强烈表明你能做的最好的事情就是试图以某种方式绕过它。
我自己没有试过这个(如果它有用会有点奇怪,不过如果我们要相信它应该的评论......),但想法是现在你有一个参考,并解决它们需要虚拟查找(yada)。
int main()
{
Base x_on_stack;
Base &x = x_on_stack;
int version = 1;
x.initAs(version);
int v = x.foo();
assert(v == 1);
return 0;
}