成员函数中的静态变量


158

有人可以解释一下成员函数中的静态变量如何在C ++中工作。

给定以下类别:

class A {
   void foo() {
      static int i;
      i++;
   }
}

如果我声明了多个实例 A,则foo()在一个实例上调用是否会i在所有实例上增加静态变量?还是只有一个被调用?

我假设每个实例都有自己的副本 i,但是逐步执行某些代码似乎并非如此。

Answers:


169

因为class A是一个非模板类,并且A::foo()是一个非模板函数。static int i该程序内部只有一个副本。

A对象的任何实例都会影响对象的i寿命,i并且整个程序的生命周期都将保持不变。要添加示例:

A o1, o2, o3;
o1.foo(); // i = 1
o2.foo(); // i = 2
o3.foo(); // i = 3
o1.foo(); // i = 4

3
感谢您的好榜样!是否有一种方法可以实际实现使static int i实例具有特定范围的东西,例如eg o1.foo(); // i = 1$o2.foo(); // i = 1...?
Stingery 2014年

14
尽管这可能不是您要查找的样式,但是将ia设为私有数据成员将具有您所描述的效果。如果您担心名称冲突,则可以添加前缀m_以指示i的状态。
卡尔·莫里斯

137

static不幸的是,关键字在C ++中具有一些不同的不相关含义

  1. 当用于数据成员时,这意味着数据是在类中而不是在实例中分配的

  2. 当用于函数内部的数据时,这意味着数据是静态分配的,在第一次进入该块时进行初始化,并持续到程序退出。此外,变量仅在函数内部可见。局部静态的这一特殊功能通常用于实现单例的惰性构造。

  3. 当在编译单元级别(模块)使用时,这意味着变量就像全局变量(即在main运行之前分配和初始化,并在main退出后销毁),但是该变量在其他编译单元中将不可访问或不可见

我在每次使用中最重要的部分上加了一些强调。不鼓励使用(3)来支持未命名的名称空间,该名称空间还允许未导出的类声明。

在您的代码中,static关键字的含义为2,与类或实例无关...它是函数的变量将只有一个副本。

正如iammilind所说,如果该函数是模板函数,则该变量可能有多个实例(因为在这种情况下,函数本身确实可以在程序中的许多不同副本中出现)。即使在这种情况下,课程和实例都不相关...请参见以下示例:

#include <stdio.h>

template<int num>
void bar()
{
    static int baz;
    printf("bar<%i>::baz = %i\n", num, baz++);
}

int main()
{
    bar<1>(); // Output will be 0
    bar<2>(); // Output will be 0
    bar<3>(); // Output will be 0
    bar<1>(); // Output will be 1
    bar<2>(); // Output will be 1
    bar<3>(); // Output will be 1
    bar<1>(); // Output will be 2
    bar<2>(); // Output will be 2
    bar<3>(); // Output will be 2
    return 0;
}

41
+1 keyword static unfortunately has a few different unrelated meanings in C++:)
iammilind

阅读本文后,世界变得更加有意义,谢谢
艾琳(Erin)

我喜欢模板的技巧。我等不及要找到借口使用它了。
托马什Zato -恢复莫妮卡

有人得到“有些人不赞成使用未命名的名称空间”的参考吗?
austinmarton 2015年

3
@austinmarton:我的版《 C ++编程语言》中有“在C ++中不建议使用static来表示'本地到翻译单元'的用法。请使用未命名的命名空间(8.2.5.1)”(1999年9月,第10版)在第819页。– 2015
6502

2

函数内部的静态变量

  • 在函数内部创建的静态变量存储在程序的静态存储器中而不是堆栈中。

  • 静态变量初始化将在函数的第一次调用时完成。

  • 静态变量将在多个函数调用中保留该值

  • 静态变量的生命周期为Program

在此处输入图片说明

例子

#include <iostream>

using namespace std;

class CVariableTesting 
{
    public:
    
    void FuncWithStaticVariable();
    void FuncWithAutoVariable();

};

void CVariableTesting::FuncWithStaticVariable()
{
    static int staticVar = 0; //staticVar is initialised by 0 the first time
    cout<<"Variable Value : "<<staticVar<<endl;
    staticVar++;
}
void CVariableTesting::FuncWithAutoVariable()
{
    int autoVar = 0;
    cout<<"Variable Value : "<<autoVar<<endl;
    autoVar++;
}
    

int main()
{
    CVariableTesting objCVariableTesting;
    cout<<"Static Variable";
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    
    cout<<endl;
    cout<<"Auto Variable";
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    
    return 0;
}

输出:

静态变量

变量值:0
变量值1
变量值2
变量值3
变量值4

自动变数

变量值:0
变量值:0
变量值:0
变量值:0
变量值:0


-2

简化答案:

静态变量,无论它们是(非模板的)class还是(非模板的)函数的成员,从技术上来说,其行为类似于全局标签,其范围限于classor或函数。


9
否。全局变量在程序启动时初始化,函数静态变量在首次使用时初始化。这是一个很大的区别。
6502

我认为这不会发生。但是,无论如何这应该是特定于编译器的。
0xbadf00d 2011年

2
然后,您会认为是错误的:C ++标准中的3.6.1指示具有静态存储持续时间的名称空间范围对象的构造在启动时发生;6.7(4)规定:“通常,……此类变量在控件第一次通过其声明时进行了初始化;此类变量在其初始化完成后被视为已初始化”。顺便说一下,这种首次使用初始化非常容易实现懒惰的单例构造。
6502

3.7.4:“如果适用,具有静态存储持续时间的块范围实体的恒定初始化(3.6.2)在首次进入其块之前执行。允许实现通过以下方式对其他块范围变量进行早期初始化:静态或线程存储持续时间,在允许实现在名称空间范围(3.6.2)中以静态或线程存储持续时间静态初始化变量的条件下。否则,在控件第一次通过其声明时初始化此类变量;”
0xbadf00d 2011年

1
然而,足够奇怪的是:1)对于常量初始化,无关紧要的是讨论是否可以在第一次进入该块之前初始化局部静态变量(该变量仅在块内部可见,并且常量初始化不会产生副作用);2)您的帖子中没有提到有关常量初始化的内容;3)局部静态数据对于非恒定初始化非常有用,例如MyClass& instance(){ static MyClass x("config.ini"); return x; }-一个单线程使用的有效可移植实现,恰恰是因为无论您说什么,局部静态数据都不只是像全局变量那样简单。
6502
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.