C++ 可恶的 duplicate symbol (符号重复)

C++ 可恶的 duplicate symbol (符号重复)

lucas Lv4

背景

我之前是主写 JavaKotlin 的, C++ 写的少之甚少

之前写 OpenGL 的时候,就一直被 duplicate symbol 这个问题困扰

现在我终于找到了解决方法

先说结论

我之所以没有明白这个问题的原因和解决方法,是因为我对于 C++ 多文件编程存在理解错误

我没有意识到 #include 的作用主要是帮助声明,而不是和 JavaKotlin 之类的语言一样使用 import 将包引入。

结论

C++ 在编译期间有一个连接的过程,这个过程能让 C++ 任意访问声明过的任意函数或者变量,所以 #include 的常见作用是将一大堆的声明导入到你的 cpp 文件中。

毕竟如果你在很多文件里都需要使用某些函数,每次都写好几行声明确实有点麻烦,而一行 #include 就可以解决这个问题

所以, #pragma once#if def 的作用主要是防止 #include 导入的声明重复

所以你更多的是检查声明以及定义的对应,往往能解决大部分问题

当然 #include 的功能不止是导入声明,也可以在 #include 里写函数的定义,这时候往往会将函数设为 static 来限制他的作用域

原理

首先,C++ 的编译器本身也是一段程序,而且不同编译器,编译成功与否可能会有细微的差别,但几乎都是相同的

  1. 如果你在两个以上文件定义同样的函数(方法),就会产生重复。

  2. 预处理阶段(Preprocessing),预处理指令会对源码文件(cpp 文件)进行临时扩充,预处理命令以 # 开头,例如: #include #define#ifdef
    对于 #define 指令,编译器将源码中的宏替换成宏定义中的内容;

    对于 #if#ifdef#ifndef 指令,编译器将有选择地跳过或选中部分源代码;

    而对于 #include 指令,编译器将把对应的库的源码插入到当前源代码中——这通常是一些通用的声明。

    #include 指令引入的头文件( .h )往往会包含大量的代码,你引入的越多,最后生成的预编译文件就越大。

    总的来说,预编译过的文件会比原来的 C++ 源码更大一些。

  3. 编译编译和汇编阶段(Compilation & assembly),预处理结束后,将预处理好的文件编译成 object 目标文,也就是 .o 文件。

  4. 链接阶段(linking),将未定义标识符的引用全部替换成它们对应的正确地址,地址对应上,这就算是链接上了,这一步就是我们代码编译过程中产生 duplicate symbol(重复)的地方。

也就是说,在编译器进行链接(linking)的时候,由于某个方法我们定义了多次,所以它并不知道要链接到那个地址,所以就报报错了

解决办法

消除 symbol

  1. method 声明为 inlineinline 方法会被直接 copy 到方法调用处,这个 Kotlin 也有类似的语法
  2. 宏定义
1
#define LOG(s) (std::cout << s << std::endl)

声明为静态

静态函数会只在当前 object目标文件(.o文件)可见

最好的方式就是养成良好的代码习惯

考虑清楚你定义的函数应该设为全局的还是局部的,是否会被多次定义

如果你定义的函数存在 .h 文件里,那么就容易出现重复定义的情况

  • Title: C++ 可恶的 duplicate symbol (符号重复)
  • Author: lucas
  • Created at : 2024-04-01 09:23:30
  • Updated at : 2024-11-12 09:51:53
  • Link: https://darkflamemasterdev.github.io/2024/04/01/C-可恶的-duplicate-symbol-符号重复/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments