关于静态变量的问题

Question about static variable

本文关键字:变量 问题 静态 于静态      更新时间:2023-10-16

我读了很多关于静态变量的文档。这里有一个我不明白的例子。假设类中的静态变量是这样声明的:

class Something
{
  public:
    static int s_nValue;   
};
int Something::s_nValue = 1;
int main()    
{    
    Something::s_nValue = 2;    
    std::cout << Something::s_nValue;    
    return 0;    
}

我的问题是:我们已经在类中声明了s_nvalue,为什么需要重新定义它?如果我们之前不写int,它会显示一个错误。为什么会这样呢?

在一个普通的c++程序中,你的类被定义在头文件中,所有使用它们的源文件都包含在头文件中。因此,如果它按照您期望的方式工作,那么每个源文件都将拥有该静态变量的自己的副本,而实际上它们应该共享一个副本。这违反了"单一定义规则"……每个对象只能定义为存在于一个地方。

因此,在类中声明变量只是向编译器宣布,在某个地方将有一个具有此名称和类型的变量;它不会指示编译器为它分配空间。此时,该变量在包含它的任何源文件中都保持未定义状态。然后,在一个特定的源文件(通常是那个特定类的实现文件)中,提供一个实际的定义,即int Something::s_nValue;行。这要求编译器为变量分配空间,以便它只存在于一个位置,并且当您将所有目标文件链接在一起时不会产生歧义。

这是c++的特性,当你定义一些东西时,你必须指定它的确切类型,即使你之前有一个声明。对于所有变量和函数都是如此。

旁注:不要重新定义它,否则会导致编译错误。你只需要定义它

声明和定义是不一样的。有时你可以同时做这两件事,但无论哪种方式,你都需要声明和定义一些东西。

为什么?

好吧,因为标准这么说,但是为什么标准这么说呢?

它与编译和链接的工作方式有关。如果我有几个源文件,a.cppb.cpp和几个头文件,a.hb.h,那么我想编译它们。通常,您单独编译所有源文件以获得a.ob.o,然后在最后将它们链接在一起以获得最终程序。

假设我们有:

// a.h =========================
class A { static int n; };  
// b.h =========================
class B { static int n; };
// a.cpp =======================
#include "a.h"
#include "b.h"
int foo() { return A::n + B::n; }
// b.cpp =======================
#include "a.h"
#include "b.h"
int bar() { return A::n - B::n; }

请记住,#include实际上只是将其他文件粘贴到包含文件中。所以编译器在编译a.cppb.cpp时看到的是:

// a.cpp =======================
class A { static int n; };
class B { static int n; };
int foo() { return A::n + B::n; }
// b.cpp =======================
class A { static int n; };
class B { static int n; };
int bar() { return A::n - B::n; }

A::nB::n应该放在哪个目标文件中?a.o还是b.o ?它在a.cppb.cpp中都声明了,所以编译器不知道把它放在哪里。如果你把它放在两者中,那么你将定义它两次,编译器将不知道使用哪个(在这种情况下,链接器会给你一个'multiply defined symbol'错误)。

这就是为什么我们需要一个定义。定义告诉我们把它放在哪个目标文件中。

// a.cpp =======================
#include "a.h"
#include "b.h"
int A::n = 0; // A::n goes in a.o
int foo() { return A::n + B::n; }
// b.cpp =======================
#include "a.h"
#include "b.h"
int B::n = 0; // B::n goes in b.o
int bar() { return A::n - B::n; }

值得指出的是,您可以将两者都放在a.cppb.cpp中。没关系,只要定义一次就行。

欢迎来到美妙的c++世界:声明vs定义。在你发布的代码中,有一个声明和一个定义。声明为符号指定了名称和类型。定义赋予符号一个"值"。

class Something
{
public:
    // declaration.
    static int s_nValue;   
};
// definition.
int Something::s_nValue = 1;

这个过程类似于功能原型:

// declaration.
void f ( int i );
// definition.
void f ( int i )
{
    std::cout << i << std::endl;
    // ...
}

更令人困惑的是,有些语句同时执行这两项操作。例如,如果您不声明函数,则定义也充当声明(这对于静态变量是不可能的,就像您发布的Something::s_nValue案例一样)。

这与C中的情况类似,在头文件中可以这样做:

extern int Something_s_nValue;

你的源文件,你会做:

int Something_s_nValue;

第一部分是头文件中的声明,第二部分是源文件中的定义。