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

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

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

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

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

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

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

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

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

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

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


当前回答

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

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

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

正确的。

它也只讨论了类成员的存储持续时间,它不是特定于实例的,这也是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.

其他回答

我不是一个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,达到了这样的效果-混淆。它用来表示不同的事情(我可以说,可能是相反的事情)。

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

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

变量:

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

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*){},并且它们都可以以不同的方式进行日志记录。

静态对象:我们可以使用关键字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