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
}
对于堆栈it works fine上的对象。但是对于使用new
(而不是malloc
)在堆上进行分配,它会给出链接器错误:
undefined reference to `vtable for A'
答案 0 :(得分: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
}
答案 1 :(得分:6)
首先,此代码不可编译,因为在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
}
问题是为什么第一个声明不产生liker错误而第二个声明产生错误。
从形式上看,您的程序无效,因为它违反了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();
}
上面的代码将在GCC中产生相同的链接器错误,即使此代码中仍未使用未定义的函数foo
。
换句话说,只需添加足够数量的代码即可使编译器相信需要对象的VMT。在这种情况下,声明之间的行为差异与C ++语言无关。这只是编译器特有的实现问题。
答案 2 :(得分:3)
你不能保持虚函数未实现,即使它是'未使用'(因为它实际上是由vtable使用的)。这是代码中的错误。
由于编译器中vtable的特殊实现,bug以这种特殊的方式表现出来。你没有实现第一个虚函数。只要看到类的第一个虚函数的实现,编译器就会插入vtable。由于没有,所以没有vtable。
如果你没有实现第二个功能,链接器会抱怨该特定功能,而不是关于vtable。
[编辑]
您的编译器可能在堆栈上优化了A
的副本,这就是链接器没有抱怨的原因。
malloc
行实际上并不引用类型A的对象,这就是它不会创建链接器问题的原因。这条线还有另一个问题:它不应该编译。 malloc
返回void*
,如果没有强制转换,则不会转换为其他类型的指针。
答案 3 :(得分:1)
如果A::foo
是A
,则该标准只需要A
的一个实现
在程序中的任何地方实例化。无论是否
实例化是通过声明局部变量或通过
一个新的表达。但是,如果此规则不需要诊断
破碎;如果您没有提供声明,或者您提供两个或更多,
它只是未定义的行为。编译器做的任何事情都是
“正确”。在这种情况下,它可能发生的是:
{{1}}的构造函数是内联的,因此初始化vptr的代码(并触发vtable的实例化)对于编译器是完全可见的,
由于编译器可以看到对象的所有用法,因此可以看到vptr从未被使用过,所以它只是抑制它。
并且没有vptr,不需要生成vtable,因此没有对虚函数的引用。
总之,它取决于编译器如何优化;你可能会收到一个错误 对于本地声明和新表达,或两者都没有, 或一个而不是另一个。它可能取决于优化 选项,或其他。就C ++而言,它可能依赖于 月亮的阶段,而不是一个错误,你可能只是得到 运行它时崩溃的代码(但我先说的是场景 最有可能的。)
答案 4 :(得分:0)
未使用无关紧要。定义所有虚拟功能。就这么简单。
您的自动存储持续时间对象(您选择在堆栈上调用对象的内容)不会[多态]使用,因此您无法进行诊断。这不是正确的。