iam*_*ind 8 c++ virtual-functions linker-errors language-lawyer
struct A
{
virtual void foo(); // unused and unimplemented
virtual void bar () {}
};
int main ()
{
A obj; // ok
obj.bar(); // <-- added this edition
A* pm = (A*)malloc(sizeof(A)); // ok
A* pn = new A; // linker error
}
Run Code Online (Sandbox Code Playgroud)
对于堆栈上的对象,它工作正常.但是对于堆上的分配new(不malloc),它会给出链接器错误:
undefined reference to `vtable for A'
Run Code Online (Sandbox Code Playgroud)
小智 9
因为malloc不会调用(或尝试在这种情况下调用)A的构造函数,而new会这样做.
此代码编译并说明GCC发生链接器错误的位置:
#include <cstdlib>
struct A
{
virtual void foo(); // unused and unimplemented
virtual void bar () {}
};
int main ()
{
A obj; // linker error
A* pm = (A*) malloc(sizeof(A)); // ok
A* pn = new A; // linker error
}
Run Code Online (Sandbox Code Playgroud)
首先,这段代码是不可编译的,因为在C++ void *中不能隐式转换为A *.需要显式强制转换.
其次,这个例子malloc完全无关紧要.malloc分配原始内存,与任何特定类型完全无关.在这种情况下,malloc知道任何A并且它不会创建类型的对象A.
出于这个原因,这个问题的真实例子应如下所示
struct A
{
virtual void foo(); // unused and unimplemented
virtual void bar () {}
};
int main ()
{
A obj; // ok
A* pn = new A; // linker error
}
Run Code Online (Sandbox Code Playgroud)
问题是为什么第一个声明不会产生任何错误,而第二个声明会产生错误.
从形式上看,您的程序无效,因为它违反了C++语言(特别是ODR)的形式要求.在实践中,两个声明都可以或应该产生相同的错误,因为在这两种情况下,对象正式需要指向VMT的指针.在这种情况下,无法创建VMT,因为某些功能未定义.但是,第一个声明只是因为编译器能够针对第一个声明优化掉所有对VMT的引用(而不是第二个声明).编译器也很可能能够优化整个obj对象,因为它不会在其他任何地方引用.
在GCC中(因为您似乎正在使用GCC),也很容易为第一个声明触发相同的错误
struct A
{
virtual void foo(); // unused and unimplemented
virtual void bar () {}
};
int main ()
{
A obj; // linker error
A *p = &obj;
p->bar();
}
Run Code Online (Sandbox Code Playgroud)
上面的代码将在GCC中产生相同的链接器错误,即使foo未在此代码中使用未定义的函数.
换句话说,只需添加足够数量的代码即可使编译器相信需要对象的VMT.在这种情况下,声明之间的行为差异与C++语言无关.它只是特定于编译器的实现问题.