一、源代码
#include<iostream>// /*structBase{virtualvoidf(){}};structDerived:Base{voidf()override;};intmain(){Base*p=new Derived;return0;}// *//* #include <memory> struct Base { virtual void f() {} //virtual ~Base() = default; }; struct Derived : Base { void f() override {} }; int main() { std::unique_ptr<Base> p = std::make_unique<Derived>(); p->f(); std::cout << " Derived class object is created and function f() is called." << std::endl; } */二、报错信息
g++ polymorph.cpp /usr/bin/ld: /tmp/ccLc7YZm.o: warning: relocation against`_ZTV7Derived'inread-only section`.text._ZN7DerivedC2Ev[_ZN7DerivedC5Ev]' /usr/bin/ld: /tmp/ccLc7YZm.o: in function `Derived::Derived()':polymorph.cpp:(.text._ZN7DerivedC2Ev[_ZN7DerivedC5Ev]+0x1f): undefined reference to `vtableforDerived' /usr/bin/ld: warning: creating DT_TEXTRELina PIE collect2: error: ld returned1exitstatus三、解析
问题在第 9 行:Derived::f()只有声明,没有定义
structDerived:Base{voidf()override;// 只声明,没有函数体};为什么报的是"undefined reference to vtable for Derived"?
原因分析
有虚函数的类都需要一张虚函数表(vtable)
构造Derived对象时,构造函数要把vptr指向vtable for Derived(链接错误里的_ZTV7Derived)。所以new Derived会让Derived::Derived()引用这张表。vtable 生成在哪个 .o 文件里?
GCC/Clang 遵循 Itanium C++ ABI 的key function(关键函数)规则:类中第一个非内联、非纯虚的虚函数就是 key function。vtable只在定义了 key function 的那个编译单元里生成。Derived的 key function 是f(),因为它是在类外定义(不是 inline)的。- 但你从来没有定义
f(),所以没有任何编译单元生成 vtable for Derived。
所以链接器找不到 vtable,就报了这个错
报错里写的是缺 vtable,而不是缺Derived::f(),因为你的代码里没有直接调用f()。真正缺失的符号是 vtable,缺f()的定义才是根本原因。
另外两条 warning:relocation ... DT_TEXTREL in a PIE也是这个未解析符号带出来的,不用单独处理。
修复方法(选一种)
方法 1:在类内给出定义
structDerived:Base{voidf()override{}};方法 2:在类外定义(通常写在 .cpp 里)
voidDerived::f(){}方法 3:确实不需要重写,就删掉这行声明
顺带提一句
Base没有虚析构函数。如果以后执行delete p;(p是Base*,实际指向Derived),行为是未定义的。建议加上:
virtual~Base()=default;源代码文件下半部分注释里那一版(第 24 行)已经写了这一行,取消注释就行。当前代码没有delete,所以还有一个内存泄漏。改用下面那版的std::unique_ptr可以同时避免这个问题。
Recap:The goal is to getpolymorph.cppto link.
It fails becauseDerived::f()is declared but never defined,
so the vtable forDerivedis never generated. Next,
add a body (void f() override {}) and recompile.