为了澄清这个问题,我宁愿将“ static”关键字的用法分为三种不同形式:
(一个)。变数
(B)。功能
(C)。类的成员变量/函数
以下是每个子标题的说明:
(A)变量的“静态”关键字
这可能有点棘手,但是如果正确解释和理解,这非常简单。
为了解释这一点,首先了解变量的范围,持续时间和链接是非常有用的,否则,通过staic关键字的模糊概念总是很难看清事情。
1.范围:确定变量在文件中的何处。它可以有两种类型:(i)Local或Block Scope。(ii)全球范围
2.持续时间:确定何时创建和销毁变量。同样,它有两种类型:(i)自动存储持续时间(用于具有Local或Block范围的变量)。(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关键字可以应用于具有局部和全局范围的变量,在两种情况下,它们的含义不同。我将首先说明具有全局范围的变量中“静态”关键字的用法(在此我还将阐明关键字“ extern”的用法),然后说明具有局部范围的变量。
1.具有全局范围的变量的静态关键字
全局变量具有静态持续时间,这意味着当使用它的特定代码块(例如main())结束时,它们不会超出范围。根据链接的不同,它们只能在声明它们的同一文件内访问(对于静态全局变量),也可以在文件外部甚至声明它们的文件外部(外部类型全局变量)访问。
对于具有外部说明符的全局变量,并且如果正在对其进行初始化的文件之外访问此变量,则必须在使用该文件的文件中将其向前声明,就像必须转发一个函数一样声明其定义是否在与使用位置不同的文件中。
相反,如果全局变量具有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,对于每个“ const-ness”,如果没有指定,则有两种默认的c ++链接情况:
(i)如果全局变量是非常量,则默认情况下其链接为extern,即,可以使用extern关键字通过向前声明在另一个.cpp文件中访问非const全局变量(换句话说,非const全局变量具有外部链接(当然具有静态持续时间)。另外,在已定义extern关键字的原始文件中使用该关键字也是多余的。在这种情况下,要使外部文件无法访问非常量全局变量,请在变量类型之前使用说明符“ static”。
(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 */
2.具有局部范围的变量的静态关键字
本地范围内变量的static关键字更新(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;
}
看看上面关于静态局部变量和静态全局变量的标准,可能会想问一下它们之间的区别是什么。尽管全局变量可以在代码中的任何点访问(取决于常量和外部,以及相同或不同的翻译单位),但不能直接访问功能块中定义的静态变量。该变量必须由函数值或引用返回。让我们通过一个例子来证明这一点:
//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
。这是一个小例子
我建议读者也来查找之间的区别constexpr
,并static const
在变量此计算器线程。以上是我对应用于变量的static关键字的解释。
B.“ static”关键字用于功能
就功能而言,static关键字具有直接的含义。在此,它是指函数的链接。
通常,默认情况下,在cpp文件中声明的所有函数都具有外部链接,即,通过向前声明,一个文件中定义的函数可在另一个cpp文件中使用。
在函数声明将其链接限制为internal之前,请使用static关键字,即,不能在其定义之外的文件内使用static函数。
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 vs非const静态成员变量
(i)非常量类静态成员变量
在前面的示例中,静态成员(公共成员和私有成员)都是非常量。ISO标准禁止在类中初始化非const静态成员。因此,与前面的示例一样,必须在类定义之后将它们初始化,并警告需要省略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/