IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> static和inline在C++头文件中的使用 -> 正文阅读

[C++知识库]static和inline在C++头文件中的使用

先上例子:

// foo.hpp
#ifndef FOO_HPP
#define FOO_HPP
void foo()
{
	//
}
#endif

// a.cpp
#include "foo.hpp"
void func_a()
{
    foo();
}

// b.cpp
#include "foo.hpp"
void func_b()
{
    foo();
}

// main.cpp
extern void func_a();
extern void func_b();
int main()
{
    func_a();
    func_b();

    return 0;
}

编译:

$ arm-linux-gnueabihf-g++ a.cpp b.cpp main.cpp -save-temps -O0 -o main
b.o: In function `foo()':
b.cpp:(.text+0x0): multiple definition of `foo()'
a.o:a.cpp:(.text+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

As is known to us all,上面这个例子链接时会报错:foo函数重复定义。因为a.cpp和b.cpp都包含了foo.hpp文件,相当于foo这个全局函数在a.cpp和b.cpp都有定义了。用nm命令就可以看到a.o和b.o中都存在相同的T _Z3foov这个符号,大写的T说明它是global(external)的,如下所示:

$ nm a.o
00000000 T _Z3foov
0000000c T _Z6func_av

$ nm b.o
00000000 T _Z3foov
0000000c T _Z6func_bv

既然foo作为一个全局函数会导致重复定义,那么自然很容易想到,假如加上static关键字限定其作用域,是不是就没有问题了呢?
尝试一下,链接ok,再看一下目标文件中的符号,a.o和b.o中都存在t _ZL3foov,哈哈,小写的t说明这就是个local的符号,所以在最终生成的main文件中存在2个地址不同的t _ZL3foov

$ nm a.o
0000000c T _Z6func_av
00000000 t _ZL3foov

$ nm b.o
0000000c T _Z6func_bv
00000000 t _ZL3foov

$ nm main
# ...
000083c8 T _start
000084b4 T _Z6func_av
000084cc T _Z6func_bv
000084c0 t _ZL3foov
000084a8 t _ZL3foov
# ...

However,由于static函数在每个编译单元都有独自一份拷贝,这也显然会造成问题。假如初衷只是将函数定义写在头文件中的话,inline则是更好的选择,详细的说明可以参考这个:Jon Lee的知乎回答。下面看看改为inline后的编译结果:_Z3foov是一个weak symbol。

$ nm a.o
00000000 W _Z3foov
00000000 T _Z6func_av

$ nm b.o
00000000 W _Z3foov
00000000 T _Z6func_bv

$ nm main
# ...
000083c8 T _start
000084b4 W _Z3foov
000084a8 T _Z6func_av
000084c0 T _Z6func_bv
# ...

BTW,在win10下用g++编译的话,会有些不同:a.o和b.o中各有T _Z3foov,最终的main.exe中则是有一个U _Z3foov和一个T _Z3foov。U代表的含义是:

该符号在当前文件中是未定义的,即该符号的定义在别的文件中。例如,当前文件调用另一个文件中定义的函数,在这个被调用的函数在当前就是未定义的;但是在定义它的文件中类型是T。但是对于全局变量来说,在定义它的文件中,其符号类型为C,在使用它的文件中,其类型为U。

$ nm a.o
#...
0000000000000000 T _Z3foov
0000000000000000 T _Z6func_av

$ nm b.o
#...
0000000000000000 T _Z3foov
0000000000000000 T _Z6func_bv

$ nm main.exe
                 U _Z3foov
00000001004016f0 T _Z3foov
0000000100401080 T _Z6func_av
00000001004010a0 T _Z6func_bv

在这个知乎专栏中也对不同编译器中inline的处理方式进行了测试,BTW,文中还测试了在不同的 .cpp 中定义相同的 inline(但不是 static)的 函数(但不是 static),会由于编译顺序不同->链接阶段的符号冲突和混乱链接->导致最终输出结果不一致的情况,说明——

inline 函数应该老老实实在 .h 中定义,而不是为了赶时间手动拷贝到不同的 .cpp 中并顺手修改,以免造成 Debug / Release 模式下结果不同

想要测试的话,把a.cpp和b.cpp做如下更改,分别用不同的编译命令(注意编译文件的顺序和优化选项)看下输出结果就明白啦。

 g++ a.cpp b.cpp main.cpp -save-temps -O0 -o main
 g++ b.cpp a.cpp main.cpp -save-temps -O0 -o main
 g++ a.cpp b.cpp main.cpp -save-temps -O3 -o main
 g++ b.cpp a.cpp main.cpp -save-temps -O3 -o main
// a.cpp
#include <iostream>
inline void foo()
{
    std::cout << "foo a" << std::endl;
}
void func_a()
{
    foo();
}

// b.cpp
#include <iostream>
inline void foo()
{
    std::cout << "foo b" << std::endl;
}
void func_b()
{
    foo();
}

Finally小结,还是Jon Lee的知乎回答说得好:

  • 谨慎使用 static:如果只是想把函数定义写在头文件中,用 inline,不要用static。
  • inline最大的用处是:非template 函数,成员或非成员,把定义放在头文件中,定义前不加inline ,如果头文件被多个translation unit(cpp文件)引用,ODR会报错multiple definition。
  • inline 函数的定义不一定要跟声明放在一个头文件里面:定义可以放在一个单独的头文件 .hxx 中,里面需要给函数定义前加上 inline 关键字,原因看下面第 2.点;然后声明 放在另一个头文件 .hh 中,此文件include 上一个 .hxx。这种用法 boost里很常见:优点1. 实现跟API 分离,encapsulation。优点2. 可以解决有关inline函数的循环调用问题。
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-24 18:17:43  更:2021-12-24 18:17:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 13:21:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码