C++编程实践—extern的应用说明

一、extern
extern关键字无论是在C语言还是在C++语言中都非常重要,作为一种重要的存储类说明符,主要用来声明一个变量或函数具有外部链接属性。即它可以在其本编译单元中进行使用,但定义不在本编译单元。其基本的定义形式如下:
externint g_init;
externintfunc();
需要说明的是,函数天然具有extern属性,所以一般不需要显式的声明为extern。
二、主要技术点
在C/C++中,extern的主要应用包括以下几种情况:
-
extern和变量
即定义不同的变量(全局、线程局部等)是非本编译单元原始定义。这个比较简单,看一个线程局部变量定义的例子://.h
externthread_localint tls_var;
//.cpp 可以跨编译单元共享名字,但每个线程都有自己的副本
thread_localint tls_var;不过需要说明的是,较老的编译器可能对tls的extern应用支持不足。
-
extern和函数
声明一个函数是外部定义,并且在C/C++混合编程中,提醒编译器不进行C++的编译改名机制。在前面已经说过了,全局函数天然具有extern属性,所以一般不用书写。但extern应用的一个重要的方面即extern “C”是在C/C++混合编程中的函数调用中处理改名机制(name mangling)://c .h 调用一个C库的头文件
// xxxso.h
#pragma once
#ifdef __cplusplus
extern"C" {
#endif
intadd(int a, int b);
voidloadData(void);
#ifdef __cplusplus
}
#endif
//c++ .cpp
#include"xxxso.h"
intmain(){
return add(10, 1);
} -
extern和模板
由于模板的分离编译问题,extern一般无法在模板应用中使用。但外部模板,extern template是一种特殊应用。这个在前面有过详细说明,此处不再赘述,请参看相关文档 -
其它
包括一些不常见的应用,比如名空间、嵌套等。如:namespace A {
namespace B {
int d = 0;
externint temp;
}
}
int A::B::temp = 1;
一般来说,掌握好前三点,基本就不会有什么大问题的出现了。一些细节可以在后期的应用中遇到再进行完善和掌握即可。
三、细节和陷阱
-
extern与static
这是一个典型的冲突,前者代表有外部属性而后者代表是内部使用。即不允许将二者同时应用到一个变量或函数中 -
extern与const
前面提到的const在C和C++中对链接属性的控制不同的问题(细节参看前文“C和C++中const的不同”) -
extern与新标准inline
其实在新标准下,如果想直接在头文件中定义全局变量,提供了inline即内联变量。在前面也分析过,应用起来更简单方法 -
extern与重载
正如前面所分析,extern “C”就是处理改名保留原始名称,如果有重载,就会生成多个同名函数,编译无法通过。
另外extern “C”这个机制无法在C语言中使用,看名字应该也明白。自己用自己,没意思。 -
extern与初始化
这点很重要,extern并不影响全局变量的初始化顺序,这也是前面反复提到的初始化顺序影响软件安全的一个基本技术点 -
extern与动态库的导出机制
这个不要误解,extern中是声明外部链接,和导出机制不是一回事
其实还有一些不太常用的细节和陷阱,比如混合头文件的处理等,这都需要开发者自己到时候要认真把控,此处就不一一展开说明。
四、例程
看一个常见的全局变量声明应用的方法:
//全局变量头文件:一般在头文件进行extern声明
// externGlobal.h
#pragma once
externint g_var;
externchar g_char;
//全局变量定义文件:在cpp文件中进行定义
// externGlobal.cpp
externint g_var = 0;
externchar g_char = 0;
//so头文件 xxxso.h
#pragma once
//对于C++调用C库函数需要显示的使用extern "C"防止改名
#ifdef __cplusplus
extern"C" {
#endif
intadd(int a, int b);
voidloadData(void);
#ifdef __cplusplus
}
#endif
//c++ .cpp
#include"xxxso.h"
#include"externGlobal.h"
intmain(){
return add(10, g_var);
}
上面的示例非常简单,但也提供了一个全局extern和extern “C”的很常见的用法。模式都是一样,只是复杂程度有所不同罢了。
五、总结
对于一些基础的关键字的用法,很多开发者看上去对其很了解,而且确实在开发不会出现问题。但这未必代表真正理解了它们的含意。在变更应用场景或相关应用机制后,有可能就会卡一下。不过,这都不是重点,这些细节是可以在真正需要时再补齐的。也就是前面提到的,“重视细节但不陷入细节”!
夜雨聆风