C++子类重写父类方法遇链接错误:multiple definition与undefined reference
问题背景
我想在geraet.cpp中重写elektronik.cpp里的schalten()方法,但执行链接命令g++ -o main main.o elektronik.o geraet.o时出现以下错误:
/usr/bin/ld: geraet.o: in function `schalten()': geraet.cpp:(.text+0x0): multiple definition of `schalten()'; elektronik.o:elektronik.cpp:(.text+0x0): first defined here /usr/bin/ld: main.o: in function `main': main.cpp:(.text+0x23): undefined reference to `Geraet::schalten()' collect2: error: ld returned 1 exit status
尝试在elektronik.h中将该方法设为virtual后,错误反而变多:
/usr/bin/ld: geraet.o: in function `schalten()': geraet.cpp:(.text+0x0): multiple definition of `schalten()'; elektronik.o:elektronik.cpp:(.text+0x0): first defined here /usr/bin/ld: main.o: warning: relocation against `_ZTV6Geraet' in read-only section `.text' /usr/bin/ld: main.o: in function `main': main.cpp:(.text+0x1e): undefined reference to `vtable for Geraet' /usr/bin/ld: main.cpp:(.text+0x2e): undefined reference to `Geraet::schalten()' /usr/bin/ld: warning: creating DT_TEXTREL in a PIE collect2: error: ld returned 1 exit status
希望解答以下问题:
- 为什么我无法直接在子类中重写该方法?
- 为什么使用
virtual方法无法解决这个问题? - 为什么链接时会出现方法未定义的错误?
代码片段
main.cpp
#include "elektronik.h" #include "geraet.h" #include <iostream> int main() { Geraet tool{}; std::cout << tool.schalten() << std::endl; return 0; }
elektronik.h
#pragma once class Elektronik { public: bool schalten(); };
elektronik.cpp
#include "elektronik.h" bool schalten() { return false; }
geraet.h
#pragma once class Geraet : public Elektronik { public: bool schalten(); };
geraet.cpp
#include "geraet.h" bool schalten() { return true; }
问题解答
1. 为什么无法直接在子类中重写该方法?
你根本没在重写方法——你的elektronik.cpp和geraet.cpp里定义的都是全局函数schalten(),而非类的成员函数。C++中定义类成员函数必须用类名::限定作用域,否则编译器会将其视为独立的全局函数,这就导致两个全局函数重名,触发链接器的multiple definition错误。
另外,即便你修正了作用域,父类方法未声明为virtual时,子类的同名方法只是隐藏父类方法,不是重写——调用时会根据变量的静态编译类型决定执行逻辑,而非动态运行类型,无法实现多态。
2. 为什么使用virtual无法解决问题?
加virtual只是让父类方法变成虚函数,但你依然没修正成员函数的定义问题——全局的schalten()仍然存在,重名冲突未解决。同时,当类包含虚函数时,编译器会生成虚表(vtable),但如果子类的虚函数没有正确定义(还是全局函数),编译器找不到Geraet::schalten()的实现,就会报undefined reference to vtable for Geraet和Geraet::schalten()未定义的错误。
3. 为什么链接时会出现方法未定义的错误?
因为链接器找不到Geraet::schalten()的真正实现——你在geraet.h里声明了这个成员函数,但geraet.cpp里写的是全局schalten(),不是Geraet::schalten()。链接器需要绑定成员函数的实现,找不到自然就会报未定义错误。
修复方案
把两个cpp文件里的函数改成类成员函数的正确定义方式:
修改后的 elektronik.cpp
#include "elektronik.h" bool Elektronik::schalten() { return false; }
修改后的 geraet.cpp
#include "geraet.h" bool Geraet::schalten() { return true; }
如果要实现真正的多态(重写),还需要把父类的schalten()声明为virtual,子类可以加override关键字明确重写:
修改后的 elektronik.h
#pragma once class Elektronik { public: virtual bool schalten(); // 声明为虚函数 };
修改后的 geraet.h
#pragma once class Geraet : public Elektronik { public: bool schalten() override; // 用override明确重写 };
这样编译链接就能正常通过,且用父类指针/引用指向子类对象时,会调用子类的实现,实现多态。
内容的提问来源于stack exchange,提问作者mabioo

