在C++中,extern主要有两个作用:(1)、extern声明一个变量或函数;(2)、extern与”C”一起连用,用于链接指定。关于extern “C”的使用可以参考: /fengbingchun/article/details/78634831 ,这里主要介绍extern声明一个变量或函数时的用法。
extern可置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量或函数时,在其它模块中寻找其定义。
如果想声明一个变量而非定义它,就在变量名称前加关键字extern,而且不要显示地初始化变量。任何包含了显示初始化的声明即成为定义。我们能给由extern关键字标记的变量赋一个初始值,但是这么做也就抵消了extern的作用。extern语句如果包含初始值就不再是声明,而变成定义了。在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误。变量能且只能被定义一次,但是可以被多次声明。声明和定义的区别看起来也许微不足道,但实际上却非常重要。如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此时,变量的定义必须出现在且只能出现在一个文件中,而其它用到该变量的文件必须对其进行声明,却绝对不能重复定义。
某些时候有这样一种const变量,它的初始值不是一个常量表达式,但又确实有必要在文件间共享。这种情况下,我们不希望编译器为每个文件分别生成独立的变量。相反,我们想让这类const对象像其它(非常量)对象一样工作,也就是说,只在一个文件中定义const,而在其它多个文件中声明并使用它。解决的办法是,对于const变量不管是声明还是定义都添加extern关键字,这样只需定义一次就可以了。如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。
在大系统中,在多个文件中实例化相同模板的额外开销可能非常严重。在新标准中,我们可以通过显示实例化(explicit instantiation)来避免这种开销。一个显示实例化有如下形式:
extern template declaration; // 实例化声明
template declaration; // 实例化定义
declaration是一个类或函数声明,其中所有模板参数已被替换为模板实参。
当编译器遇到extern模板声明时,它不会在本文件中生成实例化代码。将一个实例化声明为extern就表示承诺在程序其它位置有该实例化的一个非extern声明(定义)。对于一个给定的实例化版本,可能有多个extern声明,但必须只有一个定义。由于编译器在使用一个模板时自动对其实例化,因此extern声明必须出现在任何使用此实例化版本的代码之前。当编译器遇到一个实例化定义(与声明相对)时,它为其生成代码。
对每个实例化声明,在程序中某个位置必须有其显示的实例化定义。一个类模板的实例化定义会实例化该模板的所有成员,包括内联的成员函数。当编译器遇到一个实例化定义时,它不了解程序使用哪些成员函数。因此,与处理类模板的普通实例化不同,编译器会实例化该类的所有成员。即使我们不使用某个成员,它也会被实例化。因此,我们用来显示实例化一个类模板的类型,必须能用于模板的所有成员。
在一个类模板的实例化定义中,所用类型必须能用于模板的所有成员函数。
模板显示实例化(explicit instantiation):一个声明,为所有模板参数提供了显示实参。用来指导实例化过程。如果声明是extern的,模板将不会被实例化;否则,模板将利用指定的实参进行实例化。对每个extern模板声明,在程序中某处必须有一个非extern的显示实例化。
关键字extern为声明但不定义一个对象提供了一种方法,实际上它类似于函数声明,承诺了该对象会在其它地方被定义或者在此文本文件中的其它地方,或者在程序的其它文本文件中。extern声明不会引起内存被分配,它可以在同一文件中或同一程序的不同文件中出现多次。关键字extern也可以在函数声明中指定,唯一的影响是将该声明为隐式属性,在其它地方定义,变为显示的。extern关键字告诉编译器,”这个变量可能定义在这个模块或其它模块中”,一个extern声明并没有生成数据,它仅表明这个数据是共享的。这个变量必须是在别处定义过的,而且它只能定义一次。
以下是测试代码:
:
#ifndef FBC_CPPBASE_TEST_EXTERN_HPP_
#define FBC_CPPBASE_TEST_EXTERN_HPP_
namespace extern_ {
extern const int bufsize; // 与.cpp中定义的bufsize是同一个
int test_extern_1();
int test_extern_2();
} // namespace extern_
#endif // FBC_CPPBASE_TEST_EXTERN_HPP_
:
#include ""
#include <iostream>
extern std::string extern_variable_blog_addr;
extern std::string extern_variable_github_addr;
extern int extern_function_add(int a, int b);
extern const std::string extern_function_name();
namespace extern_ {
namespace {
int fcn() { return 0; }
}
extern double pi = 3.1415; // 定义
/* reference: C++ Primer(Fifth 中文版) pages 63
某些时候有这样一种const变量,它的初始值不是一个常量表达式,但又确实有必要在文件间共享。
这种情况下,我们不希望编译器为每个文件分别生成独立的变量。相反,我们想让这类const对象
像其它(非常量)对象一样工作,也就是说,只在一个文件中定义const,而在其它多个文件中声明
并使用它。解决的办法是,对于const变量不管是声明还是定义都添加extern关键字,这样只需定义一次就可以了
*/
extern const int bufsize = fcn();
int test_extern_1()
{
extern int i; // 声明i而非定义i
int j; // 声明并定义j
//extern double pi = 3.1415; // 定义,在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误
return 0;
}
int test_extern_2()
{
fprintf(stdout, "blob addr: %s\n", extern_variable_blog_addr.c_str());
fprintf(stdout, "github addr: %s\n", extern_variable_github_addr.c_str());
fprintf(stdout, "a + b = %d\n", extern_function_add(2, 3));
fprintf(stdout, "name: %s\n", extern_function_name().c_str());
return 0;
}
} // namespace extern_
:
#ifndef FBC_CPPBASE_TEST_EXTERN2_HPP_
#define FBC_CPPBASE_TEST_EXTERN2_HPP_
#include <string>
extern std::string extern_variable_blog_addr;
extern std::string extern_variable_github_addr;
extern int extern_function_add(int a, int b);
extern const std::string extern_function_name();
#endif // FBC_CPPBASE_TEST_EXTERN2_HPP_
:
#include ""
std::string extern_variable_blog_addr{ "/fengbingchun" };
std::string extern_variable_github_addr{ "/fengbingchun" };
int extern_function_add(int a, int b)
{
return (a + b);
}
extern const std::string extern_function_name()
{
return "C++";
}
GitHub: /fengbingchun/Messy_Test