c++ inline使函数实现可以在头文件中,避免多重定义错误
链接:https://www.zhihu.com/question/53082910/answer/133612920
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
inline 绝对是C++里最让人混淆的关键词之一了(比static还过分)。
============== Update 30 Nov 2016
看其他评论里有提到static 的。个人评价一下 static + inline 一起:那就是把死人往活里搞,活人往死里搞的赶脚,坑之深简直不忍直视。先上追加的3个结论;后面有代码,有耐心的小伙伴们拿回去自己试。
3. 谨慎使用 static:如果只是想把函数定义写在头文件中,用 inline,不要用static。static 和 inline 不一样:
- static 的函数是 internal linkage。不同编译单元可以有同名的static 函数,但该函数只对 对应的编译单元 可见。如果同一定义的 static 函数,被不同编译单元调用,每个编译单元有自己单独的一份拷贝,且此拷贝只对 对应的编译单元 可见。
- inline 的函数是 external linkage,如果被不同编译单元调用,每个编译单元引用/链接的是同一函数,同一定义。
- 上面的不同直接导致:如果函数内有 static 变量,对inline 函数,此变量对不同编译单元是共享的(Meyer's Singleton);对于static 函数,此变量不是共享的。看后面的代码就明白区别了。
4. static inline 函数,跟 static 函数单独没有差别,所以没有意义,只会混淆视听。
5. inline 函数的定义不一定要跟声明放在一个头文件里面:定义可以放在一个单独的头文件 .hxx 中,里面需要给函数定义前加上 inline 关键字,原因看下面第 2.点;然后声明 放在另一个头文件 .hh 中,此文件include 上一个 .hxx。这种用法 boost里很常见:优点1. 实现跟API 分离,encapsulation。优点2. 可以解决 有关inline 函数的 循环调用问题:这个不展开说了,看一个这个文章就懂了:Headers and Includes: Why and How 第 7 章,function inlining。
Reference: inline specifier
============== 原答案30 Nov 2016
1. 不要再把 inline 和编译器优化挂上关系了,太误导人。编译器不傻,inline is barely a request。你不加inline,小函数在开O3时,编译器也会自动给你优化了。看到inline时,应该首先想到其他用意,在考虑编译器优化。
2. inline最大的用处是:非template 函数,成员或非成员,把定义放在头文件中,定义前不加inline ,如果头文件被多个translation unit(cpp文件)引用,ODR会报错multiple definition。
============== static / inline 代码
a.hh
#ifndef A_HH
# define A_HH # include <iostream> namespace static_test
{
static int& static_value() // (!*!) Or change this to inline
{
static int value = -1;
return value;
} namespace A
{
void set_value(int val);
void print_value();
}
} #endif
a. cc
# include "a.hh" namespace static_test
{
namespace A
{
void set_value(int val)
{
auto& value = static_value();
value = val;
} void print_value()
{
std::cout << static_value() << '\n';
}
}
}
b.hh:
#ifndef B_HH
# define B_HH # include <iostream> namespace static_test
{
namespace B
{
void set_value(int val);
void print_value();
};
} #endif
b.cc:
# include "a.hh"
# include "b.hh" namespace static_test
{
namespace B
{
void set_value(int val)
{
auto& value = static_value();
value = val;
} void print_value()
{
std::cout << static_value() << '\n';
}
}
}
main. cc
# include "a.hh"
# include "b.hh" int main()
{
static_test::A::set_value(42); static_test::A::print_value();
static_test::B::print_value(); static_test::B::set_value(37); static_test::A::print_value();
static_test::B::print_value(); return 0;
}
- a.hh 中标注 (!*!) 的那行,如果是inline,输出:42,42,37,37。value 在整个程序中是个Singleton。
- 如果是 static,输出:42,-1,42,37。value 在不同编译单元是不同的拷贝,即使它被标注 static。
c++ inline使函数实现可以在头文件中,避免多重定义错误的更多相关文章
- [C/C++]在头文件中使用static定义变量意味着什么
文章出处:http://www.cnblogs.com/zplutor/ 看到有一位同学在头文件中这么写: static const wchar_t* g_str1 = - static const ...
- 将inline、template声明和定义在头文件中
如果要在要在源文件(a.cpp)中内联的展开一个函数(fun),则该源文件(a.cpp)中必须包含此函数(fun)的定义.如果要在多个文件中内联的展开fun,则在所有的源文件中都必须包含fun的定义. ...
- C++编译错误 --- 成员函数定义在 .h 文件中出现重定义错误(Error LNK 2005)
今天写了一个简单的类,定义在 .h 文件中, 类很简单就将其成员函数定义在了一起(class类后面).运行的时候出现了如下图所示的编译错误(error LNK2005) 查资料,大部分都是说需要加上 ...
- C++-模板的声明和实现为何要放在头文件中
源: http://blog.csdn.net/lqk1985/archive/2008/10/24/3136364.aspx 如何组织编写模板程序 发表日期: 1/21/2003 12:28:58 ...
- C ++模板的声明和实现为何要放在头文件中?
源: http://blog.csdn.net/lqk1985/archive/2008/10/24/3136364.aspx 如何组织编写模板程序 发表日期: 1/21/2003 12:28:58 ...
- cctype头文件中的一些内容
1. string 标准库 1.1初始化 string s1; 默认构造函数s1为空 string s2(s1); 将s2初始化为s1的一个副本 string s3(“value”); 将s3初始化为 ...
- C++内联函数、函数模板之于头文件
一.基本说明 C++标准中提到,一个编译单元是指一个.cpp文件以及它所include的所有.h文件,.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp文件为一个.obj文件 ...
- c语言头文件中定义全局变量的问题
c语言头文件中定义全局变量的问题 (转http://www.cnblogs.com/Sorean/) 先说一下,全局变量只能定义在 函数里面,任意函数,其他函数在使用的时候用extern声明.千万不要 ...
- 不包含SDK头文件, 补全API定义
/// @file main.cpp /// @brief 不包含SDK头文件, 补全API定义 #ifdef __cplusplus extern "C" { #endif /* ...
随机推荐
- iOS布局之Auto Layout
学习资源: <iOS6核心编程>自动布局部分 <iOS6范例经典>自动布局部分 Tutorial: iOS 6 Auto Layout versus Springs and S ...
- 微信小程序相关一、模仿京东静态登录页面
一.培训的第一天,基本上没有什么最新的东西,但是看到老师的代码收获的确实是不少. 1.1.首页代码很简洁,将共有的样式都提取的很好. 1.2.其次是每一个小块写样式的时候用到了好多子代选择器和后代选择 ...
- Spark之 Spark Streaming流式处理
SparkStreaming Spark Streaming类似于Apache Storm,用于流式数据的处理.Spark Streaming有高吞吐量和容错能力强等特点.Spark Streamin ...
- zabbix自动发现监控mysql
一. 数据库给只读权限 1.1 grant usage on *.* to 'zabbix'@'127.0.0.1' identified by 'zabbix'; flush privileges; ...
- Ubuntu 14.04 安装QQ2015
作为重要的技术交流工具,Ubuntu下没有QQ还是挺不方便的. 参考链接(带软件下载链接)http://www.longene.org/forum/viewtopic.php?f=6&t=30 ...
- Selenium自动化测试WebDriver下载
1. 所有版本chrome下载 是不是很难找到老版本的chrome?博主收集了几个下载chrome老版本的网站,其中哪个下载的是原版的就不得而知了. http://www.slimjet.com/ch ...
- c语言标准输入和scanf的关系
int a scanf("%d",&a); 什么意思,是从键盘读取一个数字存放到a中.错,scanf和所有从键盘获取输入数据的函数都不是直接从键盘获取数据的,而是从“标准输 ...
- js改变触发
onchange="doEmpty($(this))"
- ACM暑假培训宣讲稿
(鞠躬)感谢大家的掌声! 我上台来作这次的宣讲,首先要感谢大家的捧场(当然,这是一句玩笑话),其实吧,我要感谢一下我们ACM班老大(班长),hjh队友,是他指派我来的,给了我这个宝贵的机会.最要感谢的 ...
- Hibernate环境搭建
Hibernate的环境搭建,主要步骤分为一下四步: 首先创建一个工程,在工程里创建一个实体类User,在这个实体类中必须包含无参的构造器,和这个类对属性的存取方法(getter and setter ...