为什么基类指针指向基类中的纯虚拟方法,而不是派生类中的overdden方法


#include <iostream>
class A
{
public:
virtual ~A() = default;
virtual void foo(void) = 0;
};
class B : public A
{
private:
int x;
public:
B(int a) : x(a) {}
void foo(void) { std::cout << "B: " << x << "n"; }
};
class Foo
{
private:
A* a_ptr;
public:
Foo (B& x) { a_ptr = &x; }
A* get_ptr(void) { return a_ptr; }
void dummy(void) { std::cout << "Foo: "; std::cout << a_ptr << "t "<< typeid(*a_ptr).name() << "n"; a_ptr->foo(); std::cout << "n"; }
};
int main(void)
{
B b(10);
Foo f(b);
f.dummy();
return 0;
}

如果Foo的构造函数引用了B的对象,那么这个程序将按照我期望的方式执行,即a_ptr->foo()调用B::foo()

但是,如果构造函数被更改为按值接受参数,则a_ptr->foo()解析为A::foo(),并导致pure virtual method called exception

样本输出(通过参考:(:

Foo: 0x7fffe90a24e0      1B
B: 10

样本输出(通过值(:

Foo: 0x7fffc6bbab20      1A
pure virtual method called
terminate called without an active exception
Aborted (core dumped)

我对为什么会发生这种情况有一个模糊的预感,我正在寻找一些文献或参考文献,这些文献或参考资料可能会证明或反驳我的假设:当通过引用传递时,基类指针a_ptr指向一个实体,该实体的生存期超过了对a_ptr->foo()的调用。

但是,当通过值传递时,a_ptr指向一个临时的,该临时在构造函数退出时丢失。

我想这与AVTABLE有关,但我无法完全理解。

是的,你的怀疑是正确的。

B对象通过值传递到Foo构造函数时,它将成为构造函数的局部变量。构造函数正在保存指向该本地对象的指针,该指针在构造函数退出时超出范围。

因此,Foo::dummy()中对a_ptr->foo()的调用实际上是未定义的行为,因为a_ptr一开始甚至不指向有效对象。但是,它并没有真正崩溃,因为A::foo()不使用其this指针进行任何操作。它只是指向一个编译器定义的函数,该函数抛出pure virtual method called错误,但您没有捕捉到,因此程序终止。

您通过引用类型为a*的a_ptr来分配临时对象B。在构造函数退出时,此临时对象已被销毁。由于VTABLE也被破坏了,称为A::foo,它是纯虚拟的。所以你明白了。

最新更新