为什么虚拟方法会生成对 _sbrk 的未定义引用?

Why virtual method generates undefined reference to _sbrk?

这编译得很好:

class dummy {
};

这抱怨对 _sbrk 的未定义引用:

class dummy {
    virtual ~dummy();
};

为什么虚方法会生成对 _sbrk 的未定义引用?

我以前以为vtable是静态分配到某处的,不需要malloc

编译器:arm-none-eabi-gcc 8.0.0 最近 newlib。用 -fno-rtti -fno-exceptions -fno-unwind-tables.

编译

测试程序(boot类似于main):

class base {
public:
  virtual ~base();
};

class dummy : public base {
public:
  ~dummy();
};

base::~base() {
  __BKPT();
}

dummy::~dummy() {
  __BKPT();
}

extern "C" void _sbrk() {
  __BKPT();
}

void boot() {
  for(;;) {
    base b;
    dummy d;
  }

  return 0;
}

I used to think that vtable is allocated somewhere statically and does not require malloc.

不确定你从哪里得到这个假设:

  • 无法保证 vtables 的实现方式,标准也没有说明动态调度需要使用 vtables 来实现。这只是最常见的方法。
  • 我在你的问题中遗漏了 _sbrk 实际上被引入的任何证据,因为析构函数是 virtual

而且,我真的不明白问题的目的:如果要使用虚拟方法,无论如何都需要具有动态内存分配功能。

我几乎可以肯定 _sbrk 正在被调用,因为您对虚函数的使用已经引入了一条代码路径,以便在调用 'pure virtual function'.

时抛出异常。

查看您的地图文件,看看是什么调用了 _sbrk,是什么调用了它,等等,直到找到根。

查看此 post 了解更多信息:

p.s。 vtables 不需要动态内存分配

派生的 classes 可以有自己的 delete 运算符。此功能很少使用——根据我的经验几乎从未使用过。虚拟析构函数允许在使用 delete 表达式时调用正确的运算符 delete (delete p)。

编译器肯定会生成一个带删除的虚拟析构函数,它调用 class 特定的删除运算符,在几乎所有情况下,它恰好是全局删除运算符 (::operator delete),但它可以也被 class.

中定义的本地运算符覆盖

因为 delete 从未使用过(针对特定类型),所以永远不会调用 destructor-with-delete 自动生成的虚函数,但它仍然在 vtable 中被引用。除非你有适当的编译器和链接器支持,否则每个虚函数都在 vtable 中引用并且 vtable 至少在 class 的构造函数中使用,因此任何构造的对象都将需要 [=48] 的每个虚函数=].

如果你有适当的链接器支持,你可以只引入命名的虚函数;其他 vtable 条目可以为空,因为它们从未被引用。

编辑:标准报价

来自[class.dtor]/12

At the point of definition of a virtual destructor (including an implicit definition), the non-array deallocation function is determined as if for the expression delete this appearing in a non-virtual destructor of the destructor's class (see [expr.delete]). If the lookup fails or if the deallocation function has a deleted definition, the program is ill-formed. [ Note: This assures that a deallocation function corresponding to the dynamic type of an object is available for the delete-expression ([class.free]). — end note ]

"virtual destructor" ... 确定好像 "non virtual destructor" 包含一个表达式 是一个可能难以解码的废话:因为析构函数是虚拟的,我们为什么要谈论虚拟析构函数? (我不得不把上面的标准文本读几遍。)

另一种查看方式是,就可能的实现而言(某些实现过去正是这样做的):

每个析构函数实际上接受一个bool参数deallocate并且编译器添加代码:

if (deallocate)
  delete this;

就在析构函数的主体结束之前,所有析构函数都使用 false 参数调用,除了调用 delete 运算符时的完整对象之一。