static关键字及其在C ++中的各种用法


195

关键字static是一个在C ++中具有多种含义的关键字,我感到非常困惑,而且我永远也不会对它的实际工作方式bend之以鼻。

据我了解,存在static存储持续时间,这意味着在全局情况下,它会持续到程序的整个生命周期,但是当您谈论本地时,它意味着默认情况下将其初始化为零。

C ++标准对具有关键字的类数据成员说static

3.7.1静态存储持续时间[basic.stc.static]

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

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

局部变量是什么意思?那是函数局部变量吗?因为还存在这样的情况,当您声明局部函数时,因为该函数static仅初始化一次,因此它首次进入此函数。

它也只讨论关于类成员的存储时间,那是非实例特定的,那又是一个static否定的属性?还是存储期限?

现在static,文件范围和情况如何?默认情况下,是否所有全局变量都被认为具有静态存储持续时间?以下内容(来自3.7.1节)似乎表明了这一点:

1所有没有动态存储持续时间,没有线程存储持续时间以及非本地变量的变量都具有静态存储持续时间。这些实体的存储应在程序的整个过程中持续(3.6.2、3.6.3)

如何static涉及变量的联动?

整个static关键字令人困惑,有人可以用英语阐明它的不同用法,还能告诉我何时初始化static类成员吗?


Answers:


147

变量:

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如果可能,否则初始化为零),然后进行非初始化。按照在翻译单元中定义的顺序对本地人进行了“动态初始化” (对于诸如此类的东西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文件中使用,以确保从未在任何其他文件中使用该功能。
  • 可以放在标头中,每个文件都有它自己的函数副本。没什么用,因为内联几乎做同样的事情。
  • 通过减少工作量来缩短链接时间
  • 可以在每个翻译单元中放置一个具有相同名称的函数,并且它们都可以做不同的事情。例如,您可以static void log(const char*) {}在每个cpp文件中放置一个,它们每个都可以以不同的方式登录。

1
班级成员呢?这不是第三种情况吗?
艾蒂安

4
@Etienne-静态类数据成员与静态全局变量相同,除了可以从其他转换单元访问它们之外,任何访问(从成员函数中除外)都必须指定classname::范围。静态类成员函数就像全局函数一样,但是作用于该类,或者像普通成员一样,但是没有this(这不是选择-这两个应该等效)。
Steve314 2013年

1
@LuchianGrigore:虽然我明白你的意思,但不确定使用什么措辞。
Mooing Duck 2013年

1
@ Steve314:我理解您的意思,但是当处理一个非常静态的术语,如static时,希望我们都更加谨慎。特别是所有全局(真正是名称空间级别)变量都具有静态持续时间,因此在静态全局变量中添加静态可以理解为namespace A { static int x; },这意味着内部链接,并且与静态类数据成员的行为有很大不同。
大卫·罗德里格斯(DavidRodríguez)-德里贝斯

1
“如果它在名称空间范围内,则无法从任何其他翻译单元进行访问...”如果它在名称空间范围内,那是什么意思?并非总是这样,您能举个例子和一个反例吗?
AturSams 2015年

66

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

链接与此正交。

我认为这是您可以做出的最重要的区分。了解这一点以及其余的内容,并记住它,应该很容易(不要直接处理@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
}

整个static关键字令人困惑

当然,除非您熟悉它。:)为了避免在语言中添加新的关键字,委员会为此重复使用了IMO,即混淆。它用来表示不同的事物(我可能会说,可能是相反的事物)。


1
让我弄清楚这一点-您是说,当我static int x在名称空间范围内说时,是否为其提供了非静态存储?
迈克尔·哈加尔

29

为了澄清这个问题,我宁愿将“ 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/


1
1)在c ++ 17之前,只能在类中初始化整数静态const成员变量,例如struct Foo{static const std::string name = "cpp";};is error,name必须在类外部定义;使用在c ++ 17中引入的内联变量,可以编写代码:struct Foo{static inline const std::string name = "cpp";};2)可以使用范围解析运算符和点运算符的实例(例如:instance.some_static_method())通过类名访问公共静态成员/成员函数
oz1

“ m_anyVariable”不应该成为“ m_anyNumber”吗?在您的最后一个代码示例中?
gebbissimo

我无法判断答案的完整性和正确性,但是它看起来确实很全面,而且易于理解。非常感谢!如果您想改进它,那么开始时的简短总结可能会有所帮助,因为这是一个很长的文本,对于那些知道诸如“内部/外部”之类的术语的人来说,要点很容易显示为嵌套列表或树状图。链接”
gebbissimo

18

实际上很简单。如果在函数范围内将变量声明为静态变量,则在连续调用该函数之间将保留其值。所以:

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,因为first.a和second.a本质上是相同的变量。至于初始化,请参见此问题。


这不解决名称空间范围变量。
迈克尔·哈格

10

当您static在文件范围内声明变量时,该变量仅在特定文件中可用(从技术上来说,是* translation单元,但不要让它变得过于复杂)。例如:

cpp文件

static int x = 7;

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

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将使该函数只能从该文件访问,而不能从其他文件访问。

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

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

8

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

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”指针可以使用。没有在任何特定的类实例上调用它们。


1
“假设它是公开可用的。” - 不是。
Luchian Grigore

2
myStaticVar还需要定义。Kinda重要的是要提到,在回答有关static关键字语义的问题时,您不觉得吗?
Praetorian

@Praetorian:谢谢,固定。
Jamin Gray

1
@JaminGrey“静态独立”是指静态非成员函数,每当我仅在当前CPP文件中需要一些新功能时就编写此类函数,并且不希望链接程序必须处理其他符号。
VR

1
@VR奇怪!我从来不知道功能存在。感谢您拓宽我的知识!
Jamin Gray

1

我不是C程序员,所以我无法适当地向您提供有关C程序中使用static的信息,但是当涉及到面向对象的编程时,static基本上将变量,函数或类声明为相同。在程序的整个生命周期中。举个例子。

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完全向后兼容。


1

局部变量是什么意思?那是函数局部变量吗?

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

因为还存在当您将局部函数声明为静态函数时,它仅初始化一次,因此它首次进入此函数。

对。

它还仅讨论关于类成员的存储持续时间,它与实例无关,那又是静态no的属性吗?还是那存储期限?

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

也就是说,在所有情况下,R份额int R::a- int R::a是永远不会被复制。

现在如何处理静态和文件作用域?

有效地在适当的地方具有构造函数/析构函数的全局对象-初始化要等到访问后才进行。

静态与变量的链接有何关系?

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

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

static 还可以根据声明的位置(文件/命名空间)指定内部链接。

整个static关键字令人困惑

在C ++中,它有太多用途。

有人可以澄清英语的不同用法,还可以告诉我何时初始化静态类成员吗?

main如果已加载并具有构造函数,则会自动进行初始化。这听起来不错,但是初始化顺序在很大程度上不受您的控制,因此复杂的初始化变得很难维护,并且您希望将其最小化-如果必须具有静态属性,则函数局部可在整个库中更好地扩展。项目。对于具有静态存储持续时间的数据,应尝试最小化此设计,尤其是在可变(全局变量)的情况下。初始化的“时间”也因多种原因而有所不同-加载程序和内核有一些技巧可以最大限度地减少内存占用并推迟初始化,具体取决于所讨论的数据。


1

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

1
应当正确地指出,这些范例来自tutorialspoint.com/cplusplus/cpp_static_members.htm
BugShotGG,2016年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.