原文:https://blog.csdn.net/pi9nc/article/details/11267031

利用匿名namespace解决C++中重复定义的问题

今天写代码的时候又碰到了C++中多编译单元导致重复定义(multi definition)的链接问题。其实这个问题以前也碰到过几次,急着编译出代码也没有去深究背后的一些知识。今天系统的看了一些资料,算是把这个问题彻底搞清楚了。这里做下简单总结吧:

  • C++中有由于模版分离编译等问题,导致常常需要在头文件加入变量定义或者函数定义的代码,从而在链接多编译单元时导致multi-definition重复定义的问题。
  • 传统C语言中的static关键在C++中对于类的成员有其他的语义,导致其功能的局限性。
  • 在C++中建议使用匿名namespace类实现将一个函数或者变量的定义局限在一个编译单元内,避免multi-definition 的问题。

在C++中的,由于引入了面向对象的概念,导致了有时候在头文件中不得不加入函数实现或者变量定义的代码。比如在大部分编译器上不支持模版分离编译,导致很多模板类的实现只有放在头文件中,像boost等库都大量采用了hpp这种格式实现完全头文件化的库。

但是这种方法经常会导致一个问题就是multi-definition重复定义,在较大型的工程中往往会采用多编译单元的形式生成多个.o文件,然后再用ld链接生成可执行文件。如果多个.o中都include了同一个hpp文件,而该hpp文件又包含了全局变量,类的静态成员等一些变量的定义,那么就会导致gcc的multi-definition报错。在传统的C程序中可以通过static申明一个变量或者函数不生成全局符号来解决这个问题,但是C++中static关键字对于类的成员有了其他语义。因此C++中建议使用匿名namespace来替代static避免multi-definition的问题。看如下一个例子:

  1. ----------- test.hpp----------
  2. #include <string>
  3. class
  4. A
  5. {
  6. public:
  7. static
  8. std::string y;
  9. };
  10. std::string A::y = std::string();
  11. ----------- test_comm.cpp----------
  12. #include "test.hpp"
  13. void
  14. func() { }
  15. ----------- test_main.cpp----------
  16. #include "test.hpp"
  17. void
  18. func();
  19. int
  20. main(int
  21. argc, char
  22. *argv)
  23. {
  24. func();
  25. }

我们在g++上编译就会出现如下错误:

  1. leoxiang@SEC38_64_sles10:~$ g++ -c test_comm.cpp
  2. leoxiang@SEC38_64_sles10:~$ g++ -c test_main.cpp
  3. leoxiang@SEC38_64_sles10:~$ g++ -o test
  4. test_comm.o test_main.o
  5. test_main.o:(.bss+0x0): multiple definition of `A::y'
  6. test_comm.o:(.bss+0x0): first defined here
  7. collect2: ld returned 1 exit
  8. status

下面通过匿名namespace解决这个问题,只需要把test.hpp的实现用匿名namespace包围即可避免重复定义的问题:

  1. ----------- test.hpp----------
  2. #include <string>
  3. namespace
  4. {
  5. class
  6. A
  7. {
  8. public:
  9. static
  10. std::string y;
  11. };
  12. std::string A::y = std::string();
  13. }

实际上匿名namespace的作用是把其中的变量都放在了一个随机名字空间中,并且保证改名字空间在多个编译单元中是唯一的。因为匿名namespace中声明或定义的变量函数是全局可见的,所以并不会对自己所在文件的编译造成影响,这就是实现了之前C语言中static关键字的作用,并且具有更好的实用性。

但是匿名namespace也不是完美的,下面这篇文章《C++ 工程实践(1):慎用匿名 namespace》中介绍了使用匿名namespace会导致的两个问题:

  • 其中的函数难以设断点,如果你像我一样使用的是 gdb 这样的文本模式 debugger。
  • 使用某些版本的 g++ 时,同一个文件每次编译出来的二进制文件会变化,这让某些 build tool 失灵。
  • 同时在头文件中使用匿名空间也会对库使用这造成一些陷阱,类似与在C中在头文件中使用静态变量。

即使如此,在Google C++编程规范2.1节中也鼓励使用namespace代替C语言中的static关键字。因此个人觉得一些特殊情况下如果必须在头文件中定义类静态变量或者函数,可以考虑将整个文件代码用匿名namespace包裹,可以较好解决重复定义的问题。

【转】利用匿名namespace解决C++中重复定义的问题的更多相关文章

  1. 利用预编译解决C/C++重复定义的错误 -2020.09.13

    利用预编译解决C/C++重复定义的错误 -2020.09.13 我们现在有main.c和function.h两个文件 main.c #include <stdio.h> #include ...

  2. 浅谈利用同步机制解决Java中的线程安全问题

    我们知道大多数程序都不会是单线程程序,单线程程序的功能非常有限,我们假设一下所有的程序都是单线程程序,那么会带来怎样的结果呢?假如淘宝是单线程程序,一直都只能一个一个用户去访问,你要在网上买东西还得等 ...

  3. js 利用事件委托解决mousedown中的click

    有一个需求是这样的: 父元素div绑定一个mousedown事件,子元素a绑定一个click事件. 看解构: <div id="nav"> <a href=&qu ...

  4. 利用excel去除txt文本中重复项

    2017-04-10 1.要去重的文件,点击右键,选择程序. 2.选择excel表格或者wps表格. 3.excel表格去重:选中单元格——数据——筛选——高级筛选——选择不重复记录——确定 wps表 ...

  5. 在jsp中重复定义了两个相同id的标签导致的错误

    jQuery做前台开发的程序有一个页面在IE11和谷歌浏览器下都没有问题,但是在XP的IE8下运行就报错: 后来发现是定义了两个相同id的标签所致. 在icCard.jsp中定义的标签: 在carIn ...

  6. C++解决case中不能定义局部变量问题

    case Operation::DeviceAuthen: { std::string token = root["body"]["token"].asStri ...

  7. java web解决表单重复提交问题

    我们大家再进行web开发的时候,必不可少会遇见表单重复提交问题.今天就来给总结如何解决表单提交问题,欢迎大家交流指正. 首先我们在讨论如何解决表单重复提交问题之前先来解决三个问题:1.什么叫表单重复提 ...

  8. java web解决表单重复提交

    首先我们在讨论如何解决表单重复提交问题之前先来解决三个问题:1.什么叫表单重复提交?2.什么情况下会出现表单重复提交?3.什么情况需要避免表单重复提交? 什么叫表单提交问题,说白了,就是同一份信息,重 ...

  9. 转载:Windows下stdlib.h与glut.h中exit()函数重复定义的解决方案

    最近用到 OpenGL的第三方库Glut,碰到了exit()这个函数在stdlib.h与glut.h两个头文件中重复定义的情况,解决方案如下: 打开glut.h,找到exit()函数定义的地方(144 ...

随机推荐

  1. elasticsearch _settings

    查看所有索引的配置信息GET /_settings 查看某个索引的配置信息 GET /index_name/_settings { "my_index": { "sett ...

  2. 表单Content-Type为multipart/form-data时,后台数据的接收

    我们在写form提交表单的时候,后台大多数用request.getParameter的方式来接收前台输入的数据.但如果我们表单中提交的数据包含file文件传输的话,我们需要将Content-Type改 ...

  3. windows系统下Jenkins 持续集成安装使用

    先要下载安装Tomcat,基于Java的web项目可以通过Tomcat运行.下载Jenkins,注意要安装在Tomcat的webapps目录下.安装完Jenkins会自动通过浏览器打开http://l ...

  4. CVE-2019-11477:Linux 内核中TCP协议栈整数溢出漏洞详细分析 代码卫士 今天

    CVE-2019-11477:Linux 内核中TCP协议栈整数溢出漏洞详细分析 代码卫士 今天

  5. Mac使用brew安装nginx,并解决端口80访问权限问题

    1.安装 brew install nginx 2.修改配置文件 sudo vi /usr/local/etc/nginx/nginx.conf 修改默认的8080端口为80 修改日志文件地方 err ...

  6. std::wstring std::string w2m m2w

    static std::wstring m2w(std::string ch, unsigned int CodePage = CP_ACP) { if (ch.empty())return L&qu ...

  7. flutter编译ios的时候需要进行的操作:

    pod install  执行这个命令,安装依赖

  8. mybatis bind exception

    springboot  项目  搜了半天 网上的解决方法千篇一律,最终问题 原因 ,yml 文件路径问题 mybatis: mapper-locations: classpath:com/yang/f ...

  9. PAT 甲级 1038 Recover the Smallest Number (30 分)(思维题,贪心)

    1038 Recover the Smallest Number (30 分)   Given a collection of number segments, you are supposed to ...

  10. (九)会话跟踪技术之Cookie

    --部分摘自孤傲苍狼博客~ 一.会话的概念 会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话. 有状态会话:一个同学来过教室,下次再 ...