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

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

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

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

关键字static可以用来声明一个具有静态存储持续时间的局部变量。 在类定义中应用于类数据成员的关键字static给出了数据成员的静态存储持续时间。

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

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

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

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

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

这整个静态关键字是完全令人困惑的,有人能澄清它的不同用途的英语,并告诉我什么时候初始化一个静态类成员?


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

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”指针。它们不会被任何特定的类实例调用。


变量:

静态变量存在于定义它的翻译单元的“生命周期”中,并且:

If it's in a namespace scope (i.e. outside of functions and classes), then it can't be accessed from any other translation unit. This is known as "internal linkage" or "static storage duration". (Don't do this in headers except for constexpr. Anything else, and you end up with a separate variable in each translation unit, which is crazy confusing) If it's a variable in a function, it can't be accessed from outside of the function, just like any other local variable. (this is the local they mentioned) class members have no restricted scope due to static, but can be addressed from the class as well as an instance (like std::string::npos). [Note: you can declare static members in a class, but they should usually still be defined in a translation unit (cpp file), and as such, there's only one per class]

位置代码:

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

Before any function in a translation unit is executed (possibly after main began execution), the variables with static storage duration (namespace scope) in that translation unit will be "constant initialized" (to constexpr where possible, or zero otherwise), and then non-locals are "dynamically initialized" properly in the order they are defined in the translation unit (for things like std::string="HI"; that aren't constexpr). Finally, function-local statics will be initialized the first time execution "reaches" the line where they are declared. All static variables all destroyed in the reverse order of initialization.

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

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

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

功能

更直接的是,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
}

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

可以在cpp文件中使用,以确保该函数永远不会从任何其他文件中使用。 可以放在头文件中,每个文件都有它自己的函数副本。没什么用,因为内联做的几乎是同样的事情。 通过减少工作来加快链接时间 可以在每个翻译单元中放入同名的函数,它们都可以做不同的事情。例如,您可以在每个cpp文件中放入一个静态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完全向后兼容。


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

连杆与这个正交。

我认为这是你能做的最重要的区分。理解这一点和其他的,以及记住它,应该很容易(不是直接针对@Tony,而是将来可能会看到这篇文章的人)。

关键字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本质上是相同的变量。至于初始化,请参见这个问题。


当您在文件范围内声明一个静态变量时,那么该变量仅在该特定文件中可用(技术上是*翻译单元,但我们不要把它复杂化)。例如:

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

将非类函数标记为静态使该函数只能从该文件访问,而不能从其他文件访问。

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

对于类成员函数,将它们标记为静态意味着该函数不需要在对象的特定实例上调用(即它没有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++中有太多的用途。

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

It's automatically initialized before main if it's loaded and has a constructor. That might sound like a good thing, but initialization order is largely beyond your control, so complex initialization becomes very difficult to maintain, and you want to minimize this -- if you must have a static, then function local scales much better across libraries and projects. As far as data with static storage duration, you should try to minimize this design, particularly if mutable (global variables). Initialization 'time' also varies for a number of reasons -- the loader and kernel have some tricks to minimize memory footprints and defer initialization, depending on the data in question.


静态对象:我们可以使用关键字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. Duration:决定变量创建和销毁的时间。同样,它有两种类型:(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)静态,和(ii)外部

(现在你明白了)

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

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

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

在全局变量具有extern说明符的情况下,如果该变量在已初始化的文件之外被访问,则必须在使用它的文件中前向声明,就像如果函数的定义在与使用它的文件不同的文件中必须前向声明一样。

相反,如果全局变量具有static关键字,则不能在声明全局变量的文件中使用。

(请参见下面的例子进行说明)

eg:

//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) If a global variable is non-const, its linkage is extern by default, i.e, the non-const global variable can be accessed in another .cpp file by forward declaration using the extern keyword (in other words, non const global variables have external linkage ( with static duration of course)). Also usage of extern keyword in the original file where it has been defined is redundant. In this case to make a non-const global variable inaccessible to external file, use the specifier 'static' before the type of the variable.

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

Looking at the above criterion for static local variables and static global variables, one might be tempted to ask, what the difference between them could be. While global variables are accessible at any point in within the code (in same as well as different translation unit depending upon the const-ness and extern-ness), a static variable defined within a function block is not directly accessible. The variable has to be returned by the function value or reference. Lets demonstrate this by an example:

//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线程中查找变量的constexprand static const之间的区别。 这就是我对应用于变量的static关键字的解释。

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

就函数而言,static关键字具有简单的含义。这里,它指的是函数的连杆 通常在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++约定,使代码看起来更简洁

有关类中静态成员变量的更多示例,请参阅下面的链接 http://www.learncpp.com/cpp-tutorial/811-static-member-variables/

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

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

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

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

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

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


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

Lambda表达式遵循正常的名称查找规则,因此作用域(本地vs全局)和存储类(静态vs自动)会影响变量对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
        }
    }

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