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

时间:2017-08-19 11:10:26

标签: c++ gcc embedded vtable 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;
}

3 个答案:

答案 0 :(得分:3)

派生类可以拥有自己的删除运算符。这个功能很少使用 - 几乎从来没有我的经验。虚拟析构函数允许在使用删除表达式时调用正确的运算符删除(delete p)。

编译器肯定会生成一个虚拟析构函数with-delete,它调用类特定的操作符delete,在几乎所有情况下都恰好是全局操作符delete(::operator delete),但也可以被本地覆盖在类中定义的运算符。

由于永远不会使用delete(对于该特定类型),因此永远不会调用具有自动生成的析构函数的析构函数,但仍会在vtable中引用。除非你有适当的编译器和链接器支持,否则每个虚函数都在vtable中引用,而vtable至少在类的构造函数中使用,因此任何构造的对象都需要该类的每个虚函数。

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

编辑:标准报价

来自[class.dtor]/12

  

定义虚拟析构函数(包括   隐式定义),非数组释放函数是   确定好像是表达式delete this出现在   析构函数类的非虚析构函数(参见[expr.delete])。   如果查找失败或者释放功能已删除   定义,该计划是不正确的。 [注意:这保证了一个   对应于动态对象类型的释放函数   可用于delete-expression([class.free])。 - 结束说明]

“虚拟析构函数”......确定“非虚拟析构函数”包含表达式是一个可能难以解码的词:由于析构函数是虚拟的,我们为什么要讨论一个虚拟的析构函数? (我必须多次阅读上述标准文本。)

另一种观察方式是,就可能的实现而言(一些实现过去就是这样):

每个析构函数实际上都带有一个bool参数deallocate,编译器会添加代码:

if (deallocate)
  delete this;

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

答案 1 :(得分:-1)

我几乎可以肯定_sbrk正在被调用,因为你对虚函数的使用已经引入了一条代码路径,为一个纯虚函数的调用抛出一个例外。

在地图文件中偷看,查看调用_sbrk的内容,调用它的内容等等,直到找到根目录。

请参阅此帖子以获取更多信息:Declaring abstract class (pure virtual method) increase binary size substantially

P.S。 vtables不需要动态内存分配

答案 2 :(得分:-2)

  

我曾经认为vtable是静态分配的,不需要malloc。

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

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

而且,我真的没有达到问题的目的:如果你想使用虚方法,你无论如何都需要动态内存分配函数。