关键字static是一个在C++中具有多种含义的关键字,我发现它非常令人困惑,我永远无法理解它实际上应该如何工作.
根据我的理解,static存储持续时间,这意味着它在全局的情况下持续程序的生命周期,但是当你谈论本地时,它意味着它默认为零初始化.
C++标准对具有关键字的类数据成员说明了这一点static:
3.7.1静态存储持续时间[basic.stc.static]
3关键字static可用于声明具有静态存储持续时间的局部变量.
4应用于类定义中的类数据成员的关键字static为数据成员提供静态存储持续时间.
局部变量是什么意思?这是一个函数局部变量吗?因为还有一个当你声明一个本地函数时static,它只被初始化一次,它第一次进入这个函数.
它也只谈到关于类成员的存储持续时间,它是非实例特定的,那也是staticno 的属性?或者是存储持续时间?
那么static和文件范围的情况如何呢?是否所有全局变量都被视为默认具有静态存储持续时间?以下(来自第3.7.1节)似乎表明:
1所有没有动态存储持续时间,没有线程存储持续时间且不是本地的变量都具有静态存储持续时间.这些实体的存储应持续该计划的持续时间(3.6.2,3.6.3)
如何static与变量的链接相关?
整个static关键字是彻头彻尾的混乱,有人可以澄清英语的不同用途,并告诉我何时初始化一个static类成员?
Moo*_*uck 132
static变量存在于其定义的翻译单元的"生命周期" 中,并且:
constexpr,但可以从类和实例(如static)中解决.[注:你可以声明在类的静态成员,但他们通常应该仍然可以定义在一个翻译单元(CPP文件),正因为如此,有每类仅一个]位置代码:
static std::string namespaceScope = "Hello";
void foo() {
static std::string functionScope= "World";
}
struct A {
static std::string classScope = "!";
};
Run Code Online (Sandbox Code Playgroud)
在执行翻译单元中的任何功能之前(可能在std::string::npos开始执行之后),该翻译单元中具有静态存储持续时间(命名空间范围)的变量将是"常量初始化"(main在可能的情况下,否则为零),然后是非本地人按照他们在翻译单元中定义的顺序 "动态初始化" (对于那些constexpr不是这样的东西std::string="HI";).最后,函数本地静态将在第一次执行"到达"它们被声明的行时被初始化.所有constexpr变量都以与初始化相反的顺序销毁.
实现所有这一切的最简单方法是将所有未static初始化的静态变量设置为函数静态本地,这样可以确保在尝试使用它们时无论如何都会正确初始化所有静态/全局变量,从而阻止静态初始化订购惨败.
T& get_global() {
static T global = initial_value();
return global;
}
Run Code Online (Sandbox Code Playgroud)
要小心,因为当规范说的命名空间范围变量默认有"静态存储时间",他们指的是位"翻译单元的一生",但这并不意味着它不能在文件的外部访问.
显而易见,constexpr通常用作类成员函数,并且很少用于独立功能.
静态成员函数与常规成员函数的不同之处在于,它可以在没有类实例的情况下调用,并且由于它没有实例,因此它无法访问类的非静态成员.当你想要一个绝对不引用任何实例成员的类的函数或管理static成员变量时,静态变量很有用.
struct A {
A() {++A_count;}
A(const A&) {++A_count;}
A(A&&) {++A_count;}
~A() {--A_count;}
static int get_count() {return A_count;}
private:
static int A_count;
}
int main() {
A var;
int c0 = var.get_count(); //some compilers give a warning, but it's ok.
int c1 = A::get_count(); //normal way
}
Run Code Online (Sandbox Code Playgroud)
甲static自由函数意味着该功能将不被任何其他翻译单元被称为,因此接头可以完全忽略它.这有几个目的:
static在每个cpp文件中放入一个,并且每个文件都可以以不同的方式登录.Luc*_*ore 63
静态存储持续时间意味着变量在程序的生命周期内驻留在内存中的相同位置.
链接与此正交.
我认为这是你能做出的最重要的区别.理解这一点,其余的,以及记住它,应该变得容易(不是直接解决@Tony,而是将来可能会读到这个的人).
关键字static可用于表示内部链接和静态存储,但实质上这些是不同的.
局部变量是什么意思?这是一个函数局部变量吗?
是.无论何时初始化变量(在第一次调用函数时以及执行路径到达声明点时),它都将在程序的生命周期内驻留在内存中的相同位置.在这种情况下,static给它静态存储.
那么静态和文件范围的情况呢?是否所有全局变量都被视为默认具有静态存储持续时间?
是的,根据定义,所有全局变量都具有静态存储持续时间(现在我们已经清除了这意味着什么).但是命名空间范围的变量没有声明static,因为这会给它们内部链接,因此每个翻译单元都有一个变量.
静态如何与变量的链接相关?
它为命名空间范围的变量提供内部链接.它为成员和局部变量提供静态存储持续时间.
让我们扩展所有这些:
//
static int x; //internal linkage
//non-static storage - each translation unit will have its own copy of x
//NOT A TRUE GLOBAL!
int y; //static storage duration (can be used with extern)
//actual global
//external linkage
struct X
{
static int x; //static storage duration - shared between class instances
};
void foo()
{
static int x; //static storage duration - shared between calls
}
Run Code Online (Sandbox Code Playgroud)
整个静态关键字完全令人困惑
当然,除非你熟悉它.:)试图避免在语言中添加新的关键词,委员会重新使用了这个,IMO,这种效果 - 混乱.它用来表示不同的东西(可能我会说,可能是反对的东西).
ggu*_*lia 24
为了澄清这个问题,我宁愿用三种不同的形式对'static'关键字的用法进行分类:
(一个).变量
(B).功能
(C).成员变量/类的功能
每个小标题的解释如下:
(A)变量的'static'关键字
这个可能有点棘手,但如果解释和理解得当,它非常简单.
为了解释这一点,首先了解变量的范围,持续时间和链接是非常有用的,没有这些变量,通过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
Run Code Online (Sandbox Code Playgroud)
现在出现了Linkage的概念.当在一个文件中定义的全局变量打算在另一个文件中使用时,变量的链接起着重要作用.
全局变量的链接由关键字指定:(i)静态,和(ii)extern
(现在你得到解释;-))
static关键字可以应用于具有本地和全局范围的变量,在这两种情况下,它们意味着不同的东西.我将首先解释在具有全局范围的变量中使用'static'关键字(其中我还阐明了关键字'extern'的用法),然后解释了具有局部范围的变量.
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; }
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
现在c ++中的任何变量都可以是const或非const,对于每个'const-ness',我们得到两种默认的c ++链接,如果没有指定:
(i)如果一个全局变量是非const的,默认情况下它的链接是extern,即非const全局变量可以通过使用extern关键字的前向声明在另一个.cpp文件中访问(换句话说,非const全局变量)变量具有外部链接(当然具有静态持续时间)).在原始文件中使用extern关键字也是多余的.在这种情况下,要使外部文件无法访问非const全局变量,请在变量类型之前使用说明符"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
Run Code Online (Sandbox Code Playgroud)
接下来,我们将研究在不同文件中访问时上述全局变量的行为方式.
//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 */
Run Code Online (Sandbox Code Playgroud)
2.具有本地范围的变量的静态关键字
之前我提到过具有局部范围的变量具有自动持续时间,即它们在输入块时存在(无论是正常块,还是功能块),当块结束时不再存在,长话短说,变量具有本地范围具有自动持续时间和自动持续时间变量(和对象)没有链接意味着它们在代码块外部不可见.
如果静态说明符应用于块中的局部变量,它会将变量的持续时间从自动更改为静态,并且其生命周期是程序的整个持续时间,这意味着它具有固定的内存位置,并且其值仅初始化为一次在程序启动之前,如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;
}
Run Code Online (Sandbox Code Playgroud)
以上是对应用于变量的static关键字的解释.pheww!
B.用于函数的'static'关键字
在函数方面,static关键字具有直接的含义.这里,它指的是函数的链接 通常,在cpp文件中声明的所有函数默认都有外部链接,即在一个文件中定义的函数可以通过前向声明在另一个cpp文件中使用.
在函数声明限制其与内部的链接之前使用static关键字,即静态函数不能在其定义之外的文件中使用.
C. Staitc关键字用于成员变量和类的功能
1.类的成员变量的'static'关键字
我在这里直接开始一个例子
//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;
}
Run Code Online (Sandbox Code Playgroud)
在此示例中,静态变量m_designNum保留其值,并且此单个私有成员变量(因为它是静态的)与对象类型DesignNumber的所有变量共享b/w
与其他成员变量一样,类的静态成员变量也不与任何类对象相关联,这可以通过在main函数中打印anyNumber来演示
const vs非类中的非const静态成员变量
(i)非const类静态成员变量 在前面的例子中,静态成员(公共和私有)都是非常量.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指针,所以可以使用main函数中的类名和范围解析运算符来调用它们(ClassName :: functionName();)
第三,类的静态成员函数只能访问类的静态成员变量,因为类的非静态成员变量必须属于类对象.
有关类中静态成员函数的更多示例,请从learncpp.com查找以下链接
http://www.learncpp.com/cpp-tutorial/812-static-member-functions/
Mac*_*ski 18
它实际上非常简单.如果在函数范围内将变量声明为static,则在对该函数的连续调用之间保留其值.所以:
int myFun()
{
static int i=5;
i++;
return i;
}
int main()
{
printf("%d", myFun());
printf("%d", myFun());
printf("%d", myFun());
}
Run Code Online (Sandbox Code Playgroud)
将显示678而不是666,因为它会记住递增的值.
对于静态成员,它们在类的实例中保留它们的值.所以下面的代码:
struct A
{
static int a;
};
int main()
{
A first;
A second;
first.a = 3;
second.a = 4;
printf("%d", first.a);
}
Run Code Online (Sandbox Code Playgroud)
将打印4,因为first.a和second.a基本上是相同的变量.至于初始化,请看这个问题.
当你static在文件范围声明一个变量时,那个变量只能在那个特定的文件中使用(技术上,*翻译单元,但是不要太复杂).例如:
a.cpp
static int x = 7;
void printax()
{
cout << "from a.cpp: x=" << x << endl;
}
Run Code Online (Sandbox Code Playgroud)
b.cpp
static int x = 9;
void printbx()
{
cout << "from b.cpp: x=" << x << endl;
}
Run Code Online (Sandbox Code Playgroud)
main.cpp中:
int main(int, char **)
{
printax(); // Will print 7
printbx(); // Will print 9
return 0;
}
Run Code Online (Sandbox Code Playgroud)
对于局部变量,static意味着变量将被零初始化并在调用之间保留其值:
unsigned int powersoftwo()
{
static unsigned lastpow;
if(lastpow == 0)
lastpow = 1;
else
lastpow *= 2;
return lastpow;
}
int main(int, char **)
{
for(int i = 0; i != 10; i++)
cout << "2^" << i << " = " << powersoftwo() << endl;
}
Run Code Online (Sandbox Code Playgroud)
对于类变量,它意味着该类的所有成员之间只共享该变量的单个实例.根据权限,可以使用其完全限定名称从类外部访问该变量.
class Test
{
private:
static char *xxx;
public:
static int yyy;
public:
Test()
{
cout << this << "The static class variable xxx is at address "
<< static_cast<void *>(xxx) << endl;
cout << this << "The static class variable yyy is at address "
<< static_cast<void *>(&y) << endl;
}
};
// Necessary for static class variables.
char *Test::xxx = "I'm Triple X!";
int Test::yyy = 0;
int main(int, char **)
{
Test t1;
Test t2;
Test::yyy = 666;
Test t3;
};
Run Code Online (Sandbox Code Playgroud)
将非类函数标记为static使得该函数只能从该文件访问,并且无法从其他文件访问.
a.cpp
static void printfilename()
{ // this is the printfilename from a.cpp -
// it can't be accessed from any other file
cout << "this is a.cpp" << endl;
}
Run Code Online (Sandbox Code Playgroud)
b.cpp
static void printfilename()
{ // this is the printfilename from b.cpp -
// it can't be accessed from any other file
cout << "this is b.cpp" << endl;
}
Run Code Online (Sandbox Code Playgroud)
对于类成员函数,将它们标记为static意味着不需要在对象的特定实例上调用该函数(即它没有this指针).
class Test
{
private:
static int count;
public:
static int GetTestCount()
{
return count;
};
Test()
{
cout << this << "Created an instance of Test" << endl;
count++;
}
~Test()
{
cout << this << "Destroyed an instance of Test" << endl;
count--;
}
};
int Test::count = 0;
int main(int, char **)
{
Test *arr[10] = { NULL };
for(int i = 0; i != 10; i++)
arr[i] = new Test();
cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;
// now, delete them all except the first and last!
for(int i = 1; i != 9; i++)
delete arr[i];
cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;
delete arr[0];
cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;
delete arr[9];
cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
静态变量在类的每个实例之间共享,而不是每个类都有自己的变量.
class MyClass
{
public:
int myVar;
static int myStaticVar;
};
//Static member variables must be initialized. Unless you're using C++11, or it's an integer type,
//they have to be defined and initialized outside of the class like this:
MyClass::myStaticVar = 0;
MyClass classA;
MyClass classB;
Run Code Online (Sandbox Code Playgroud)
'MyClass'的每个实例都有自己的'myVar',但共享相同的'myStaticVar'.实际上,您甚至不需要MyClass的实例来访问"myStaticVar",您可以在类之外访问它,如下所示:
MyClass::myStaticVar //Assuming it's publicly accessible.
Run Code Online (Sandbox Code Playgroud)
当在函数内部用作局部变量(而不是作为类成员变量)时,static关键字会做出不同的事情.它允许您创建持久变量,而不提供全局范围.
int myFunc()
{
int myVar = 0; //Each time the code reaches here, a new variable called 'myVar' is initialized.
myVar++;
//Given the above code, this will *always* print '1'.
std::cout << myVar << std::endl;
//The first time the code reaches here, 'myStaticVar' is initialized. But ONLY the first time.
static int myStaticVar = 0;
//Each time the code reaches here, myStaticVar is incremented.
myStaticVar++;
//This will print a continuously incrementing number,
//each time the function is called. '1', '2', '3', etc...
std::cout << myStaticVar << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
就持久性而言,它是一个全局变量......但没有全局范围/可访问性.
您还可以拥有静态成员函数.静态函数基本上是非成员函数,但在类名的命名空间内,并且具有对类成员的私有访问权.
class MyClass
{
public:
int Func()
{
//...do something...
}
static int StaticFunc()
{
//...do something...
}
};
int main()
{
MyClass myClassA;
myClassA.Func(); //Calls 'Func'.
myClassA.StaticFunc(); //Calls 'StaticFunc'.
MyClass::StaticFunc(); //Calls 'StaticFunc'.
MyClass::Func(); //Error: You can't call a non-static member-function without a class instance!
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当你调用一个成员函数时,有一个名为'this'的隐藏参数,它是一个指向调用该函数的类实例的指针.静态成员函数没有那个隐藏参数......它们可以在没有类实例的情况下调用,但也无法访问类的非静态成员变量,因为它们没有"this"指针可以使用.它们不会在任何特定的类实例上调用.
| 归档时间: |
|
| 查看次数: |
155387 次 |
| 最近记录: |