为了澄清这个问题,我宁愿将“静态”关键字的用法分为三种不同的形式:
(一种)。变量
(乙)。职能
(C)。类的成员变量/函数
每个子标题的解释如下:
(A) 变量的“静态”关键字
这可能有点棘手,但是如果解释和理解得当,它就很简单了。
为了解释这一点,首先了解变量的范围、持续时间和链接是非常有用的,没有它们总是很难通过 staic 关键字的模糊概念看到
1. 范围:确定在文件中的什么位置,变量是可访问的。它可以有两种类型:(i)本地或块范围。(ii)全球范围
2. 持续时间:确定变量的创建和销毁时间。同样它有两种类型:(i)自动存储持续时间(对于具有本地或块范围的变量)。(ii)静态存储持续时间(用于具有全局范围的变量或具有静态说明符的局部变量(在函数中或代码块中))。
3. 链接:确定一个变量是否可以在另一个文件中被访问(或链接)。同样(幸运的是)它有两种类型:(i)内部链接
(对于具有块作用域和全局作用域/文件作用域/全局命名空间作用域的变量)(ii)外部链接(对于仅具有全局作用域/文件作用域/的变量)全局命名空间范围)
让我们参考下面的示例,以更好地理解普通的全局和局部变量(没有具有静态存储持续时间的局部变量):
//main file
#include <iostream>
int global_var1; //has global scope
const global_var2(1.618); //has global scope
int main()
{
//these variables are local to the block main.
//they have automatic duration, i.e, they are created when the main() is
// executed and destroyed, when main goes out of scope
int local_var1(23);
const double local_var2(3.14);
{
/* this is yet another block, all variables declared within this block are
have local scope limited within this block. */
// all variables declared within this block too have automatic duration, i.e,
/*they are created at the point of definition within this block,
and destroyed as soon as this block ends */
char block_char1;
int local_var1(32) //NOTE: this has been re-declared within the block,
//it shadows the local_var1 declared outside
std::cout << local_var1 <<"\n"; //prints 32
}//end of block
//local_var1 declared inside goes out of scope
std::cout << local_var1 << "\n"; //prints 23
global_var1 = 29; //global_var1 has been declared outside main (global scope)
std::cout << global_var1 << "\n"; //prints 29
std::cout << global_var2 << "\n"; //prints 1.618
return 0;
} //local_var1, local_var2 go out of scope as main ends
//global_var1, global_var2 go out of scope as the program terminates
//(in this case program ends with end of main, so both local and global
//variable go out of scope together
现在是联动的概念。当一个文件中定义的全局变量打算在另一个文件中使用时,变量的链接起着重要作用。
全局变量的链接由关键字指定:(i) static和 (ii) extern
(现在你明白了)
static 关键字可以应用于具有局部和全局范围的变量,在这两种情况下,它们的含义不同。我将首先解释“静态”关键字在具有全局范围的变量中的用法(我还将阐明关键字“外部”的用法),然后再解释具有局部范围的变量。
1. 全局范围变量的静态关键字
全局变量具有静态持续时间,这意味着当使用它的特定代码块(例如 main() )结束时它们不会超出范围。根据链接的不同,它们可以仅在声明它们的同一文件内访问(对于静态全局变量),或者在文件外部甚至在声明它们的文件之外访问(外部类型全局变量)
对于具有 extern 说明符的全局变量,并且如果在初始化它的文件之外访问该变量,则必须在使用它的文件中前向声明,就像函数必须前向一样如果它的定义位于与使用它的位置不同的文件中,则声明它。
相反,如果全局变量具有 static 关键字,则不能在已声明它的文件之外使用它。
(请参阅下面的示例以进行说明)
例如:
//main2.cpp
static int global_var3 = 23; /*static global variable, cannot be
accessed in anyother file */
extern double global_var4 = 71; /*can be accessed outside this file linked to main2.cpp */
int main() { return 0; }
main3.cpp
//main3.cpp
#include <iostream>
int main()
{
extern int gloabl_var4; /*this variable refers to the gloabal_var4
defined in the main2.cpp file */
std::cout << global_var4 << "\n"; //prints 71;
return 0;
}
现在 c++ 中的任何变量都可以是 const 或非常量,对于每个“const-ness”,我们得到两种默认 c++ 链接的情况,以防未指定:
(i)如果一个全局变量是非常量的,它的链接默认是外部的,即非常量全局变量可以在另一个.cpp文件中通过使用extern关键字前向声明来访问(换句话说,非常量全局变量具有外部链接(当然具有静态持续时间))。在已定义的原始文件中使用 extern 关键字也是多余的。在这种情况下,要使外部文件无法访问非常量全局变量,请在变量类型之前使用说明符“静态”。
(ii)如果全局变量为 const,则其链接默认为静态,即 const 全局变量不能在其定义位置以外的文件中访问,(换句话说,const 全局变量具有内部链接(具有静态持续时间)当然))。此外,使用 static 关键字来防止 const 全局变量在另一个文件中被访问是多余的。在这里,要使 const 全局变量具有外部链接,请在变量类型之前使用说明符“extern”
这是具有各种链接的全局范围变量的摘要
//globalVariables1.cpp
// defining uninitialized vairbles
int globalVar1; // uninitialized global variable with external linkage
static int globalVar2; // uninitialized global variable with internal linkage
const int globalVar3; // error, since const variables must be initialized upon declaration
const int globalVar4 = 23; //correct, but with static linkage (cannot be accessed outside the file where it has been declared*/
extern const double globalVar5 = 1.57; //this const variable ca be accessed outside the file where it has been declared
接下来我们研究上述全局变量在不同文件中访问时的行为。
//using_globalVariables1.cpp (eg for the usage of global variables above)
// Forward declaration via extern keyword:
extern int globalVar1; // correct since globalVar1 is not a const or static
extern int globalVar2; //incorrect since globalVar2 has internal linkage
extern const int globalVar4; /* incorrect since globalVar4 has no extern
specifier, limited to internal linkage by
default (static specifier for const variables) */
extern const double globalVar5; /*correct since in the previous file, it
has extern specifier, no need to initialize the
const variable here, since it has already been
legitimately defined perviously */
- 具有本地范围的变量的静态关键字
本地范围内变量的静态关键字更新(2019 年 8 月)
这进一步可以细分为两类:
(i) 功能块内变量的静态关键字,以及(ii) 未命名局部块内变量的静态关键字。
(i) 功能块内变量的静态关键字。
之前,我提到具有局部范围的变量具有自动持续时间,即它们在进入块时存在(无论是普通块,还是功能块),并且在块结束时不再存在,长话短说,变量具有本地范围的自动持续时间和自动持续时间变量(和对象)没有链接,这意味着它们在代码块之外不可见。
如果将静态说明符应用于功能块中的局部变量,它将变量的持续时间从自动更改为静态,并且其生命周期是程序的整个持续时间,这意味着它具有固定的内存位置并且其值仅被初始化如cpp 参考中所述,在程序启动之前一次(初始化不应与赋值混淆)
让我们看一个例子。
//localVarDemo1.cpp
int localNextID()
{
int tempID = 1; //tempID created here
return tempID++; //copy of tempID returned and tempID incremented to 2
} //tempID destroyed here, hence value of tempID lost
int newNextID()
{
static int newID = 0;//newID has static duration, with internal linkage
return newID++; //copy of newID returned and newID incremented by 1
} //newID doesn't get destroyed here :-)
int main()
{
int employeeID1 = localNextID(); //employeeID1 = 1
int employeeID2 = localNextID(); // employeeID2 = 1 again (not desired)
int employeeID3 = newNextID(); //employeeID3 = 0;
int employeeID4 = newNextID(); //employeeID4 = 1;
int employeeID5 = newNextID(); //employeeID5 = 2;
return 0;
}
看看上述静态局部变量和静态全局变量的标准,人们可能会想问,它们之间的区别是什么。虽然全局变量可以在代码中的任何位置访问(在相同以及不同的翻译单元中,取决于const和extern),但在功能块中定义的静态变量不能直接访问。变量必须由函数值或引用返回。让我们通过一个例子来证明这一点:
//localVarDemo2.cpp
//static storage duration with global scope
//note this variable can be accessed from outside the file
//in a different compilation unit by using `extern` specifier
//which might not be desirable for certain use case.
static int globalId = 0;
int newNextID()
{
static int newID = 0;//newID has static duration, with internal linkage
return newID++; //copy of newID returned and newID incremented by 1
} //newID doesn't get destroyed here
int main()
{
//since globalId is accessible we use it directly
const int globalEmployee1Id = globalId++; //globalEmployeeId1 = 0;
const int globalEmployee2Id = globalId++; //globalEmployeeId1 = 1;
//const int employeeID1 = newID++; //this will lead to compilation error since newID++ is not accessible direcly.
int employeeID2 = newNextID(); //employeeID3 = 0;
int employeeID2 = newNextID(); //employeeID3 = 1;
return 0;
}
关于选择静态全局和静态局部变量的更多解释可以在这个stackoverflow线程上找到
(ii) 未命名本地块中变量的静态关键字。
一旦本地块超出范围,就不能在块外访问本地块(不是功能块)内的静态变量。这条规则没有任何警告。
//localVarDemo3.cpp
int main()
{
{
const static int static_local_scoped_variable {99};
}//static_local_scoped_variable goes out of scope
//the line below causes compilation error
//do_something is an arbitrary function
do_something(static_local_scoped_variable);
return 0;
}
C++11 引入了constexpr
保证在编译时对表达式求值并允许编译器优化代码的关键字。现在,如果某个范围内的静态 const 变量的值在编译时已知,则代码会以类似于constexpr
. 这是一个小例子
我还建议读者在这个 stackoverflow 线程中查找变量之间constexpr
的区别。这结束了我对应用于变量的静态关键字的解释。static const
B. 用于函数的“静态”关键字
就功能而言,static 关键字具有直截了当的含义。这里指的是函数的链接
通常在一个cpp文件中声明的所有函数默认都有外部链接,即一个文件中定义的函数可以通过前向声明在另一个cpp文件中使用。
在函数声明之前使用 static 关键字会限制其与 internal 的链接,即不能在其定义之外的文件中使用静态函数。
C. Staitc 关键字用于类的成员变量和函数
1.类成员变量的'static'关键字
我这里直接举个例子
#include <iostream>
class DesignNumber
{
private:
static int m_designNum; //design number
int m_iteration; // number of iterations performed for the design
public:
DesignNumber() { } //default constructor
int getItrNum() //get the iteration number of design
{
m_iteration = m_designNum++;
return m_iteration;
}
static int m_anyNumber; //public static variable
};
int DesignNumber::m_designNum = 0; // starting with design id = 0
// note : no need of static keyword here
//causes compiler error if static keyword used
int DesignNumber::m_anyNumber = 99; /* initialization of inclass public
static member */
enter code here
int main()
{
DesignNumber firstDesign, secondDesign, thirdDesign;
std::cout << firstDesign.getItrNum() << "\n"; //prints 0
std::cout << secondDesign.getItrNum() << "\n"; //prints 1
std::cout << thirdDesign.getItrNum() << "\n"; //prints 2
std::cout << DesignNumber::m_anyNumber++ << "\n"; /* no object
associated with m_anyNumber */
std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 100
std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 101
return 0;
}
在此示例中,静态变量 m_designNum 保留其值,并且此单个私有成员变量(因为它是静态的)与对象类型 DesignNumber 的所有变量共享
与其他成员变量一样,类的静态成员变量不与任何类对象关联,这可以通过在主函数中打印 anyNumber 来证明
类中的 const 与非 const 静态成员变量
(i) 非常量类静态成员变量
在前面的示例中,静态成员(公共和私有)是非常量的。ISO 标准禁止在类中初始化非常量静态成员。因此,与前面的示例一样,它们必须在类定义之后初始化,需要注意的是需要省略 static 关键字
(ii) 类的 const-static 成员变量
这很简单,符合其他 const 成员变量初始化的约定,即类的 const 静态成员变量可以在声明时初始化,也可以在结束时初始化类声明的一个警告,即在类定义之后初始化时,需要将关键字 const 添加到静态成员中。
但是,我建议在声明时初始化 const 静态成员变量。这符合标准 C++ 约定,使代码看起来更干净
有关类中静态成员变量的更多示例,请查看 learncpp.com
http://www.learncpp.com/cpp-tutorial/811-static-member-variables/的以下链接
2.类成员函数的'static'关键字
就像类的成员变量可以是静态的一样,类的成员函数也可以。类的普通成员函数总是与类类型的对象相关联。相反,类的静态成员函数不与该类的任何对象相关联,即它们没有*this 指针。
其次,由于类的静态成员函数没有 *this 指针,因此可以在主函数中使用类名和作用域解析运算符调用它们(ClassName::functionName();)
第三,类的静态成员函数只能访问类的静态成员变量,因为类的非静态成员变量必须属于类对象。
有关类中静态成员函数的更多示例,请从 learncpp.com 查找以下链接
http://www.learncpp.com/cpp-tutorial/812-static-member-functions/
2021 年 4 月更新:static
关键字和 lambda 表达式
Lambda 表达式遵循正常的名称查找规则,因此范围(本地与全局)和存储类(静态与自动)会影响 lambda 表达式如何使用变量
- 非静态全局变量可用于局部范围内的 lambda 表达式。
//global member
int i=10;
int main(){
[]{std::cout << i;}();
//prints 10
}
- 出现在相同或不同范围内的 lambda 表达式不能使用非静态局部变量。在这种情况下,正如我们通常习惯的那样,变量必须通过值或引用来捕获
int main(){
int i{11};
[]{std::cout << i;}(); //compiler error
[i]{std::cout << i;}(); //capture by value; correct
//or
[&i]{std::cout << i;}(); //capture by reference; correct
}
- 静态变量局部作用域的静态变量可以被相同或更低/子作用域内的 lambda 表达式捕获
int main(){
static int i{12};
[]{std::cout << i;}(); //prints 12
{
[]{std::cout << i;}();//also prints 12
}
}
但是,如前所述,无法在范围外访问未命名范围内的静态变量