作者: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. 自动删除 Elasticsearch 索引

    #!/bin/bash # author: Wang XiaoQiang# crontab -e# 0 0 * * * /root/script/del_esindex.sh # auto delet ...

  2. Python 调用阿里云 API 收集 ECS 数据

    #!/usr/bin/env python # coding: utf-8 # author: Wang XiaoQiang ''' 功能介绍: 1.调用阿里云API,收集所有区域 ECS 信息 2. ...

  3. EXPLAIN sql优化方法(2) Using temporary ; Using filesort

    优化GROUP BY语句   默认情况下,MySQL对所有GROUP BY col1,col2...的字段进行排序.这与在查询中指定ORDER BY col1,col2...类似.因此,如果显式包括一 ...

  4. pid文件的作用

    pid文件的作用 一.pid文件的作用 1.pid文件的内容用cat命令查看,可以看到内容只有一行,记录了该进程的ID 2.pid文件的作用防止启动多个进程副本 3.pid文件的原理进程运行后会给.p ...

  5. 解决Eclipse编辑JavaScript时卡的问题

    eclipse在开发JavaEE项目时容易卡,特别是在编辑JavaScript时,经过网上各种搜索,综合整理一下,对自己的eclipse设置之后,结果不在出现卡的问题了. 原文地址:http://bl ...

  6. 搭建Git Server - Centos+Gitosis

    参考并部分转载自:http://www.pfeng.org/archives/757 1. 安装依赖 yum -y install curl-devel expat-devel gettext-dev ...

  7. Sqlserver2005中的varchar,varchar,char,nchar的比较

    C#窗体中的TextBox 的MaxLength:与Nvarchar类似,不论是什么,最多只能为2.我我11我1

  8. Codeforces 678E 状压DP

    题意:有n位选手,已知n位选手之间两两获胜的概率,问主角(第一个选手)最终站在擂台上的概率是多少? 思路:一看数据范围肯定是状压DP,不过虽然是概率DP,但是需要倒着推:我们如果正着推式子的话,初始状 ...

  9. MYSQL中GROUP BY的细节及SELECT语句顺序

    一.GROUP BY语句的细节 我们知道,在sql中,GROUP BY语句主要用来给数据分组,以便能对每个组进行聚集计算,但是GROUP BY也有一些限制需要知道: 1. GROUP BY字句可以包含 ...

  10. vmware Selinux配置错误,导致无法启动虚拟机

    Linux 开机提示kernel panic - not syncing: Attempted to kill init! 解决方法: 系统启动的时候,按下‘e’键进入grub编辑界面,编辑grub菜 ...