一、inline的介绍
inline这个关键字,估计只要学习过c++的都知道,在函数的应用中,就有内联函数这一个用法(具体用法请参阅以前的文章,此处不展开)。内联函数保证了函数在每个编译单元中都有一个相同的副本,通过牺牲代码体积来换取时间开销的方法一定是可以让每个人有比较深刻的印象的。那么,有些人一定会问,“有没有内联变量?”,还真有,在c++17中就推出了这个inline变量。为什么需要inline变量,这就和一些实际中c++的应用有关系了。
一般来说,任何语言的编程都要尽量保持着风格和思想的一致,即使可能这些功能有着不小的差别。但是,在c++的应用中,全局变量、类的静态成员变量(非const)的初始定义和初始化,始终保持着和其它变量不一致的套路,而这种套路,自然就会增加学习成本。而这些小的学习成本不断的堆积起来,就形成了人们对c++的一个普遍认知“这玩意儿太难了”!
下面,就从静态内部成员变量的角度来分析一下这个inline关键字。
二、c++中静态成员的普通用法
先看一下在c++17以前,类成员的静态变量的声明和定义的方式:
//student.h
class Student{
public:
Student(char *name);
public:
static int total_; //静态成员变量
private:
char * name_;
};
//student.cpp
#include "student.h"
int Student::total_ = 0;//推荐在cpp中定义
int main()
{
Student::total_ = 100;
std::cout<<"Student total is:"<<Student::total_<<std::endl;
return 0;
}
如果说在c++11以前,这似乎还没有太大问题,毕竟所有的类成员变量都不允许在类的声明中直接初始化。但从c++11以后,普通的类的成员变量都可以在类的声明中直接初始化了,而静态成员变量(除const static)外,却仍然延袭老的固有的应用方式,就显得有点土味儿了。正所谓,有需求,就会有变化,c++17就给它增加了一个关键字inline,实现了同质化的操作。这样,应用起来好学好记。
三、c++17中新的用法
再看一下在新标准下的用法:
全局变量:
Header file "example.h":
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include <atomic>
// function included in multiple source files must be inline
inline int sum(int a, int b)
{
return a + b;
}
// variable with external linkage included in multiple source files must be inline
inline std::atomic<int> counter(0);
#endif
Source file #1:
#include "example.h"
int a()
{
++counter;
return sum(1, 2);
}
Source file #2:
#include "example.h"
int a() // yet another function with name `a`
{
++counter;
return sum(3, 4);
}
int b()
{
++counter;
return sum(5, 6);
}
静态变量:
struct X
{
inline static int n = 1;
};
在c++17中,可以结合constexpr 和thread_local一起,同样也可以达到统一风格样式的目的:
1、constexpr
//c++17前
struct D
{
static constexpr int n = 5;
};
//C++17后
struct D
{
inline static constexpr int n = 5;
};
上面的这两种定义方式,基本是等价的,换句话说,constexptr在c++17中就等价于inline。但是,如果在c++17前使用引用或者取地址时,仍然需要在编译单元(cpp文件)使用constexpr int D:: n;此类定义方式。
2、thread_local
//C++17以前,在单个CPP编译单元应用
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
thread_local unsigned int rage = 1;
std::mutex cout_mutex;
void increase_rage(const std::string& thread_name)
{
++rage; // modifying outside a lock is okay; this is a thread-local variable
std::lock_guard<std::mutex> lock(cout_mutex);
std::cout << "Rage counter for " << thread_name << ": " << rage << '\n';
}
int main()
{
std::thread a(increase_rage, "a"), b(increase_rage, "b");
{
std::lock_guard<std::mutex> lock(cout_mutex);
std::cout << "Rage counter for main: " << rage << '\n';
}
a.join();
b.join();
}
//c++17以后,通过使用inline可以在多个编译单元使用
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
inline thread_local unsigned int rage = 1;
//其下代码相同......
其实,从上面的应用可以看到c++17这个引入这个inline变量,同样是使用了内联的相关原则,主要是为了避免c++编译过程中违反ODR原则(单定义法则)。另外需要注意的是在静态类成员变量是本身类型时,仍然需要使用早期的定义方式。不要使用inline这个内联变量的方式来定义使用。
- 代码主要来自https://en.cppreference.com/w/cpp/language/static#Static_data_members
四、总结
新的c++标准推出的速度越来越快,编译器的支持有点跟不上脚步。换句话说,看了标准文档,一般初学者还真搞不定编译环境。但这并不影响学习新标准,开拓视野的想法。新的visual studio 2022马上就要推出了,估计对c++20的支持一定非常丰满,热烈盼望中!