作者:Jon Lee
链接: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使函数实现可以在头文件中,避免多重定义错误的更多相关文章

  1. [C/C++]在头文件中使用static定义变量意味着什么

    文章出处:http://www.cnblogs.com/zplutor/ 看到有一位同学在头文件中这么写: static const wchar_t* g_str1 = - static const ...

  2. 将inline、template声明和定义在头文件中

    如果要在要在源文件(a.cpp)中内联的展开一个函数(fun),则该源文件(a.cpp)中必须包含此函数(fun)的定义.如果要在多个文件中内联的展开fun,则在所有的源文件中都必须包含fun的定义. ...

  3. C++编译错误 --- 成员函数定义在 .h 文件中出现重定义错误(Error LNK 2005)

    今天写了一个简单的类,定义在 .h 文件中, 类很简单就将其成员函数定义在了一起(class类后面).运行的时候出现了如下图所示的编译错误(error LNK2005) 查资料,大部分都是说需要加上 ...

  4. C++-模板的声明和实现为何要放在头文件中

    源: http://blog.csdn.net/lqk1985/archive/2008/10/24/3136364.aspx 如何组织编写模板程序 发表日期: 1/21/2003 12:28:58 ...

  5. C ++模板的声明和实现为何要放在头文件中?

    源: http://blog.csdn.net/lqk1985/archive/2008/10/24/3136364.aspx 如何组织编写模板程序 发表日期: 1/21/2003 12:28:58 ...

  6. cctype头文件中的一些内容

    1. string 标准库 1.1初始化 string s1; 默认构造函数s1为空 string s2(s1); 将s2初始化为s1的一个副本 string s3(“value”); 将s3初始化为 ...

  7. C++内联函数、函数模板之于头文件

    一.基本说明 C++标准中提到,一个编译单元是指一个.cpp文件以及它所include的所有.h文件,.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp文件为一个.obj文件 ...

  8. c语言头文件中定义全局变量的问题

    c语言头文件中定义全局变量的问题 (转http://www.cnblogs.com/Sorean/) 先说一下,全局变量只能定义在 函数里面,任意函数,其他函数在使用的时候用extern声明.千万不要 ...

  9. 不包含SDK头文件, 补全API定义

    /// @file main.cpp /// @brief 不包含SDK头文件, 补全API定义 #ifdef __cplusplus extern "C" { #endif /* ...

随机推荐

  1. 关于IP4上WIFI设置静态IP的一点经验

    一开始我设置IP4的WIFI的"静态"IP地址后,又查看了一下"BootP"或者"DHCP"选项,然后保存退出(关键错误,后有说明),再进W ...

  2. ascii编码转utf8编码,适用于python2

    def ascii2utf8(ascii): line = eval(("'" + ascii.strip() + "'")) return line

  3. Kafka核心概念(转)

    转自:https://blog.csdn.net/liyiming2017/article/details/82805479 1.Kafka集群结构 实际上kafka的结构图是有些区别的,现在我们看下 ...

  4. C#给图片加文字和图片的水印

    /// <summary> /// WaterMark 的摘要说明 /// </summary> /// 图片加水印 /// <param name="strC ...

  5. ARGB_8888

    原文出处:http://www.cnblogs.com/and_he/archive/2012/12/22/argb.html A:透明度 R:红色 G:绿 B:蓝 Bitmap.Config ARG ...

  6. OpenCV学习记录(一):使用haar分类器进行人脸识别 标签: opencv脸部识别c++ 2017-07-03 15:59 26人阅读

    OpenCV支持的目标检测的方法是利用样本的Haar特征进行的分类器训练,得到的级联boosted分类器(Cascade Classification).OpenCV2之后的C++接口除了Haar特征 ...

  7. JSON_UNESCAPED_UNICODE

    JSON_UNESCAPED_UNICODE(中文不转为unicode)

  8. [JAVA] 小数转百分数

    import java.text.NumberFormat; //获取格式化对象 NumberFormat format = NumberFormat.getPercentInstance(); // ...

  9. spring.net事件的注入

    .c#代码 TestObject source = new TestObject(); TestEventHandler eventListener1 = new TestEventHandler() ...

  10. mybatis :xml文件中传入参数和if标签结合使用时要点

    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.Reflecti ...