静态关键字及其在c++中的各种用途

关键字static在c++中有多种含义,我觉得非常困惑,我永远无法思考它实际上应该如何工作。

根据我的理解,有static存储持续时间,这意味着它在全局对象的情况下持续程序的生命周期,但当你谈论局部对象时,这意味着默认情况下它是零初始化的。

c++标准对关键字static的类数据成员这样说:

3.7.1静态存储时长[basic.stc.static]

关键字static可以用来声明一个具有静态存储持续时间的局部变量。

在类定义中应用于类数据成员的关键字static给出了数据成员的静态存储持续时间。

局部变量是什么意思?这是一个函数局部变量吗?因为当你将一个函数声明为static时,它只初始化了一次,即它第一次进入这个函数。

它也只讨论了关于类成员的存储持续时间,它不是特定于实例的呢,这也是static的一个属性不是吗?或者是储存时间?

现在,关于static和文件作用域的情况呢?默认情况下,是否所有全局变量都被认为具有静态存储持续时间?以下(来自第3.7.1节)似乎表明了这一点:

所有没有动态存储持续时间,没有线程存储持续时间,并且不是当地的的变量都有静态存储持续时间。这些实体的存储应在程序期间持续(3.6.2,3.6.3)。

static与变量的链接有什么关系?

整个static关键字完全令人困惑,有人能澄清它的不同英文用法,并告诉我初始化一个static类成员吗?

253715 次浏览

静态变量在类的每个实例之间共享,而不是每个类都有自己的变量。

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;
'MyClass'的每个实例都有自己的'myVar',但共享相同的'myStaticVar'。 事实上,你甚至不需要MyClass的实例来访问'myStaticVar',你可以像这样在类之外访问它:

MyClass::myStaticVar //Assuming it's publicly accessible.

当在函数中作为局部变量(而不是类成员变量)使用时,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;
}

就持久性而言,这是一个全局变量……但在范围/可访问性上没有全球性。

你也可以有静态成员函数。静态函数基本上是非成员函数,但是在类名的命名空间内,并且具有对类成员的私有访问。

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;
}
当你调用一个成员函数时,有一个隐藏的形参叫做“this”,这是一个指向调用该函数的类实例的指针。 静态成员函数有一个隐藏形参…它们可以在没有类实例的情况下被调用,但也不能访问类的非静态成员变量,因为它们没有“this”指针。它们不会在任何特定的类实例上被调用

变量:

static变量存在于“生命周期”;定义它的翻译单元,和:

  • 如果它在命名空间范围内(即在函数和类之外),则不能从任何其他翻译单元访问它。这就是所谓的“内部联系”。或“静态存储时间”。(不要在头文件中这样做,除了constexpr。任何其他的,你最终在每个翻译单元中都有一个单独的变量,这非常令人困惑)
  • 如果它是变量在函数中,就不能从函数外部访问它,就像任何其他局部变量一样。(这是他们提到的当地)
  • 由于static,类成员的作用域没有限制,但可以从类和实例寻址(如std::string::npos)。[注意:你可以在类中声明静态成员,但在翻译单元(cpp文件)中它们通常仍然应该是定义,因此,每个类只有一个静态成员]

位置代码:

static std::string namespaceScope = "Hello";
void foo() {
static std::string functionScope= "World";
}
struct A {
static std::string classScope = "!";
};

在翻译单元中的任何函数执行之前(可能是在main开始执行之后),该翻译单元中具有静态存储持续时间(命名空间范围)的变量将被“常量初始化”。(在可能的情况下为constexpr,否则为0),然后非局部变量将被“动态初始化”;适当地按照它们在翻译单元中定义的顺序(用于像std::string="HI";这样不是constexpr的东西)。最后,函数局部静态将在第一次执行“到达”时初始化。声明它们的行。所有static变量都按照相反的初始化顺序销毁。

要做到这一点,最简单的方法是将所有未被constexpr初始化的静态变量初始化为函数静态局部变量,这将确保在您尝试使用它们时,无论如何都正确地初始化了所有静态/全局变量,从而防止静态初始化顺序惨败

T& get_global() {
static T global = initial_value();
return global;
}

要小心,因为当规范说名称空间作用域变量具有“静态存储持续时间”时;默认情况下,它们表示“翻译单元的生命周期”。位,但意味着它不能在文件外部访问。

功能

更直接的是,static通常用作类成员函数,很少用于独立函数。

静态成员函数与常规成员函数的不同之处在于,它可以在没有类实例的情况下被调用,并且由于它没有实例,所以它不能访问类的非静态成员。当你想为一个绝对不引用任何实例成员的类创建一个函数,或者用于管理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
}

static自由函数意味着该函数不会被任何其他翻译单元引用,因此链接器可以完全忽略它。这有几个目的:

  • 可以在cpp文件中使用,以确保该函数永远不会从任何其他文件中使用。
  • 可以放在头文件中,每个文件都有它自己的函数副本。没什么用,因为内联做的几乎是同样的事情。
  • 通过减少工作来加快链接时间
  • 可以在每个翻译单元中放入同名的函数,它们都可以做不同的事情。例如,你可以在每个cpp文件中放入static void log(const char*) {},并且它们都可以以不同的方式进行登录。

我不是一个C程序员,所以我不能给你关于静态在C程序中正确使用的信息,但当涉及到面向对象编程时,静态基本上声明一个变量,或一个函数或一个类在程序的整个生命周期中是相同的。举个例子。

class A
{
public:
A();
~A();
void somePublicMethod();
private:
void somePrivateMethod();
};

当你在Main中实例化这个类时,你可以这样做。

int main()
{
A a1;
//do something on a1
A a2;
//do something on a2
}

这两个类实例彼此完全不同,彼此独立操作。但如果你像这样重新创建A类。

class A
{
public:
A();
~A();
void somePublicMethod();
static int x;
private:
void somePrivateMethod();
};

让我们再回到主体部分。

int main()
{
A a1;
a1.x = 1;
//do something on a1
A a2;
a2.x++;
//do something on a2
}

那么a1和a2将共享同一个int x副本,因此a1中对x的任何操作都将直接影响a2中x的操作。如果我要这么做

int main()
{
A a1;
a1.x = 1;
//do something on a1
cout << a1.x << endl; //this would be 1
A a2;
a2.x++;
cout << a2.x << endl; //this would be 2
//do something on a2
}

类A的两个实例共享静态变量和函数。希望这能回答你的问题。我有限的C语言知识允许我说,将函数或变量定义为静态意味着它只对函数或变量定义为静态的文件可见。但这个问题最好由一个C男来回答,而不是我。c++允许用C和c++两种方式将变量声明为静态,因为它与C完全向后兼容。

静态存储持续时间意味着变量在程序的整个生命周期内都驻留在内存中的相同位置。

连杆与这个正交。

我认为这是你能做的最重要的区分。理解这一点和其他的,以及记住它,应该很容易(不是直接称呼@托尼,但谁可能在未来看到这篇文章)。

关键字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
}

整个静态关键字完全令人困惑

当然,除非你对它很熟悉。:)为了避免在语言中添加新的关键词,委员会重新使用了这个词,IMO,达到了这样的效果-混淆。它用来表示不同的事情(我可以说,可能是相反的事情)。

其实很简单。如果在函数的作用域中将变量声明为静态变量,则它的值将在对该函数的连续调用之间保留。所以:

int myFun()
{
static int i=5;
i++;
return i;
}
int main()
{
printf("%d", myFun());
printf("%d", myFun());
printf("%d", myFun());
}

将显示678而不是666,因为它会记住增加的值。

至于静态成员,它们在类的实例之间保存它们的值。下面的代码:

struct A
{
static int a;
};
int main()
{
A first;
A second;
first.a = 3;
second.a = 4;
printf("%d", first.a);
}

将打印4,因为首先。一个和第二个。A本质上是相同的变量。至于初始化,请参见这个问题。

当你在文件范围内声明一个static变量时,那么这个变量只能在特定文件中可用(技术上来说,是*翻译单元,但我们不要把它搞得太复杂)。例如:

a.cpp

static int x = 7;


void printax()
{
cout << "from a.cpp: x=" << x << endl;
}

b.cpp

static int x = 9;


void printbx()
{
cout << "from b.cpp: x=" << x << endl;
}

main.cpp:

int main(int, char **)
{
printax(); // Will print 7
printbx(); // Will print 9


return 0;
}

对于当地的变量,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;
}

对于变量,这意味着该类的所有成员之间只共享该变量的一个实例。根据权限的不同,可以从类外部使用完全限定名访问变量。

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;
};

将非类函数标记为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;
}

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;
}

对于类成员函数,将它们标记为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;
}

局部变量是什么意思?这是一个函数局部变量吗?

是-非全局,如函数局部变量。

因为当你将一个函数声明为静态时它只初始化了一次,也就是它第一次进入这个函数时。

正确的。

它也只讨论了类成员的存储持续时间,它不是特定于实例的,这也是static的一个属性?或者是储存时间?

class R { static int a; }; // << static lives for the duration of the program

也就是说,R的所有实例共享int R::a——int R::a永远不会被复制。

那么静态作用域和文件作用域的情况呢?

有效地,在适当的地方具有构造函数/析构函数的全局变量——初始化不会延迟到访问。

静态与变量的联系是怎样的?

对于局部函数,它是外部函数。访问:函数可以访问它(当然,除非您返回它)。

对于类来说,它是外部的。访问:应用标准访问说明符(公共、受保护、私有)。

static也可以指定内部链接,这取决于它声明的位置(文件/命名空间)。

整个静态关键字完全令人困惑

它在c++中有太多的用途。

有人能澄清它英语的不同用途,并告诉我什么时候初始化一个静态类成员?

如果它被加载并且有构造函数,它会在main之前自动初始化。这听起来可能是一件好事,但初始化顺序在很大程度上超出了您的控制,因此复杂的初始化变得非常难以维护,您希望最小化这一点——如果您必须有一个静态的,那么局部函数在库和项目之间的伸缩性会更好。对于具有静态存储持续时间的数据,应该尽量减少这种设计,特别是在可变(全局变量)的情况下。初始化的“时间”也有很多不同的原因——加载器和内核有一些技巧来最小化内存占用和延迟初始化,这取决于所讨论的数据。

我们可以使用static关键字定义类成员static。当我们将类的成员声明为静态时,这意味着无论创建了多少个类的对象,都只有一个静态成员的副本。

静态成员由类的所有对象共享。在创建第一个对象时,如果没有其他初始化,则所有静态数据都初始化为零。我们不能把它放在类定义中,但它可以在类外初始化,就像下面的例子中那样,通过重新声明静态变量,使用范围解析操作符::来确定它属于哪个类。

让我们尝试下面的例子来理解静态数据成员的概念:

#include <iostream>


using namespace std;


class Box
{
public:
static int objectCount;
// Constructor definition
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
// Increase every time object is created
objectCount++;
}
double Volume()
{
return length * breadth * height;
}
private:
double length;     // Length of a box
double breadth;    // Breadth of a box
double height;     // Height of a box
};


// Initialize static member of class Box
int Box::objectCount = 0;


int main(void)
{
Box Box1(3.3, 1.2, 1.5);    // Declare box1
Box Box2(8.5, 6.0, 2.0);    // Declare box2


// Print total number of objects.
cout << "Total objects: " << Box::objectCount << endl;


return 0;
}

当上面的代码被编译和执行时,它会产生以下结果:

Constructor called.
Constructor called.
Total objects: 2

静态函数 通过将函数成员声明为静态,可以使它独立于类的任何特定对象。即使类不存在对象,并且只使用类名和作用域解析操作符::.

访问静态函数,也可以调用静态成员函数

静态成员函数只能从类外部访问静态数据成员、其他静态成员函数和任何其他函数。

静态成员函数有一个类作用域,它们不能访问类的this指针。您可以使用静态成员函数来确定类的某些对象是否已经创建。

让我们尝试下面的例子来理解静态函数成员的概念:

#include <iostream>


using namespace std;


class Box
{
public:
static int objectCount;
// Constructor definition
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
// Increase every time object is created
objectCount++;
}
double Volume()
{
return length * breadth * height;
}
static int getCount()
{
return objectCount;
}
private:
double length;     // Length of a box
double breadth;    // Breadth of a box
double height;     // Height of a box
};


// Initialize static member of class Box
int Box::objectCount = 0;


int main(void)
{


// Print total number of objects before creating object.
cout << "Inital Stage Count: " << Box::getCount() << endl;


Box Box1(3.3, 1.2, 1.5);    // Declare box1
Box Box2(8.5, 6.0, 2.0);    // Declare box2


// Print total number of objects after creating object.
cout << "Final Stage Count: " << Box::getCount() << endl;


return 0;
}

当上面的代码被编译和执行时,它会产生以下结果:

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2

为了澄清这个问题,我宁愿把“static”关键字的用法分为三种不同的形式:

(一)变量

(B)功能

(C)类的成员变量/函数

下面是对每个子标题的解释:

(一)变量的'static'关键字

这一点可能有点棘手,但如果解释和理解得当,它是相当简单的。

要解释这一点,首先了解范围,持续时间是非常有用的 和变量的的联动,没有这个东西总是很难看到 通过模糊概念的静态关键字

1. 范围:确定变量在文件中的何处可访问。它可以是两种类型:(i) 局部或块作用域。(2) 全局作用域

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) 静态, and, (ii) 走读生

(现在你明白了)

Static关键字可以应用于具有局部和全局作用域的变量,在这两种情况下,它们的含义不同。我将首先解释“static”关键字在具有全局作用域的变量中的用法(在这里我还澄清了关键字“extern”的用法),然后解释那些具有局部作用域的变量。

1. 全局作用域变量的静态关键字

全局变量具有静态持续时间,这意味着当使用它的特定代码块(例如main())结束时,它们不会超出作用域。根据链接的不同,它们要么只能在声明它们的同一个文件中访问(对于静态全局变量),要么只能在声明它们的文件之外访问(extern类型全局变量)

在全局变量具有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变量,对于每个'const-ness',在没有指定的情况下,我们得到两种默认的c++链接:

(i) 如果一个全局变量不是const,它的链接默认是extern,即非const全局变量可以在另一个.cpp文件中通过使用extern关键字前向声明来访问(换句话说,非const全局变量具有外部链接(当然是静态持续时间))。另外,在原始文件中使用extern关键字是多余的。 在本例中要使外部文件无法访问非const全局变量,请在变量的类型前使用说明符'static'.

.

.

.

(ii) 如果全局变量是const,默认情况下它的链接是静态的,即const全局变量只能在定义它的文件中访问,(换句话说,const全局变量具有内部链接(当然是静态持续时间))。此外,使用static关键字来防止在另一个文件中访问const全局变量也是多余的。 在这里,要使const全局变量具有外部链接,请在变量的类型前使用说明符'extern' < / p >

下面是具有各种链接的全局作用域变量的摘要

//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 */
  1. 局部作用域变量的静态关键字

更新(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;
}

看看上面关于静态局部变量和静态全局变量的标准,有人可能会问,它们之间的区别是什么。虽然全局变量可以在代码中的任何位置访问(根据常量-ness和走读生-ness,可以在相同或不同的翻译单元中访问),但在函数块中定义的静态变量不能直接访问。变量必须由函数值或引用返回。让我们通过一个例子来证明这一点:

//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线程中查找__abc0和static const之间的差异。 以上就是我对static关键字应用于变量的解释

B。'static'关键字用于函数

就函数而言,static关键字具有简单的含义。这里,it 表示函数的连杆 通常在cpp文件中声明的所有函数默认情况下都具有外部链接,即一个文件中定义的函数可以通过前向声明在另一个cpp文件中使用

在函数声明之前使用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的变量共享

同样像其他成员变量一样,类的静态成员变量不与任何类对象相关联,这可以通过在main函数中打印anyNumber来证明

类中的Const和非Const静态成员变量

(i)非const类静态成员变量 在前面的例子中,静态成员(包括public和private)都是非常量。ISO标准禁止在类中初始化非const静态成员。因此,就像前面的例子一样,它们必须在类定义之后初始化,但需要注意的是,static关键字需要被省略

(ii)类的常量static成员变量 这很简单,并且与其他const成员变量初始化的惯例一致,即类可以的const静态成员变量在声明点初始化,它们可以在类声明的末尾初始化,但需要注意的是,在类定义之后初始化时,需要将关键字const添加到静态成员中

但是,我建议在声明时初始化const static成员变量。这符合标准的c++约定,使代码看起来更简洁

有关类中静态成员变量的更多示例,请查阅以下来自learncpp.com的链接 http://www.learncpp.com/cpp-tutorial/811-static-member-variables/ < / p >

2. 类的成员函数的'static'关键字

就像类的成员变量可以是静态的,类的成员函数也可以是静态的。类的普通成员函数总是与类类型的对象相关联。相比之下,类的静态成员函数与类的任何对象都没有关联,即它们没有*this指针。

其次,由于类的静态成员函数没有*this指针,因此可以使用主函数中的类名和作用域解析操作符来调用它们(ClassName::functionName();)

第三,类的静态成员函数只能访问类的静态成员变量,因为类的非静态成员变量必须属于类对象。

有关类中静态成员函数的更多示例,请参阅下面的链接

http://www.learncpp.com/cpp-tutorial/812-static-member-functions/


2021年4月更新:static关键字和lambda表达式

Lambda表达式遵循正常的名称查找规则,因此作用域(本地vs全局)和存储类(静态vs自动)会影响变量对Lambda表达式的可用性

  1. 非静态全局变量可用于局部作用域中的lambda表达式。
    //global member
int i=10;
    

int main(){
[]{std::cout << i;}();
//prints 10
}
  1. 非静态局部变量对于出现在相同或不同作用域内的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
}
  1. 局部作用域的静态变量可以由相同或更低/子作用域内的lambda表达式捕获
    int main(){
static int i{12};
[]{std::cout << i;}(); //prints 12
{
[]{std::cout << i;}();//also prints 12
}
}

然而,如前所述,未命名作用域中的静态变量不能在作用域之外访问