这可以优雅地编译:
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;
}
答案 0 :(得分:3)
派生类可以拥有自己的删除运算符。这个功能很少使用 - 几乎从来没有我的经验。虚拟析构函数允许在使用删除表达式时调用正确的运算符删除(delete p
)。
编译器肯定会生成一个虚拟析构函数with-delete,它调用类特定的操作符delete,在几乎所有情况下都恰好是全局操作符delete(::operator delete
),但也可以被本地覆盖在类中定义的运算符。
由于永远不会使用delete
(对于该特定类型),因此永远不会调用具有自动生成的析构函数的析构函数,但仍会在vtable中引用。除非你有适当的编译器和链接器支持,否则每个虚函数都在vtable中引用,而vtable至少在类的构造函数中使用,因此任何构造的对象都需要该类的每个虚函数。
如果您有适当的链接器支持,则只能引入已命名的虚拟函数;其他vtable条目可以为null,因为它们从未被引用。
编辑:标准报价
在定义虚拟析构函数(包括 隐式定义),非数组释放函数是 确定好像是表达式
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。
不确定你从哪里得到这个假设:
_sbrk
virtual
实际上已被拉入
而且,我真的没有达到问题的目的:如果你想使用虚方法,你无论如何都需要动态内存分配函数。