有人能解释一下C++中成员函数中的静态变量是如何工作的。
考虑到以下类别:
1 2 3 4 5 6
| class A {
void foo() {
static int i;
i++;
}
} |
如果我声明多个A实例,那么在一个实例上调用foo()是否会增加所有实例上的静态变量i?还是只有那个电话?
我假设每个实例都有自己的i副本,但我已经指出了一些代码。
由于class A是非模板类,A::foo()是非模板函数。程序中只有一个static int i的副本。
A对象的任何实例都会影响同一个i,i的生存期将贯穿整个程序。要添加示例:
1 2 3 4 5
| A o1, o2, o3;
o1.foo(); // i = 1
o2.foo(); // i = 2
o3.foo(); // i = 3
o1.foo(); // i = 4 |
- 谢谢你的好榜样!是否有一种方法可以实际实现某个特定于实例的static int i范围,从而使o1.foo(); // i = 1和$o2.foo(); // i = 1…?
- 虽然这可能不是您要查找的样式,但将I作为类A的私有数据成员将具有您所描述的效果。如果您担心名称冲突,可以添加一个前缀,如m_,以指示i的状态。
遗憾的是,EDCOX1的6个词在C++中有几个不同的含义。
当用于数据成员时,这意味着数据是在类中分配的,而不是在实例中分配的。
当用于函数内部的数据时,这意味着数据是静态分配的,在第一次输入块时初始化,并持续到程序退出为止。此外,变量仅在函数内部可见。局部静力学的这个特殊特性经常被用来实现单例的惰性构造。
当在编译单元级别(模块)使用时,它意味着变量就像一个全局变量(即,在运行main之前分配和初始化,在main退出之后销毁),但在其他编译单元中该变量将不可访问或不可见。
我对每种使用中最重要的部分进行了强调。不鼓励使用(3)来支持未命名的命名空间,这些命名空间还允许未导出的类声明。
在您的代码中,static关键字与意义数字2一起使用,与类或实例无关…它是函数的一个变量,只有一个副本。
然而,正如iammilind所说的,如果函数是一个模板函数,那么该变量可能有多个实例(因为在这种情况下,函数本身可以在程序的许多不同副本中出现)。即使在这种情况下,类和实例也不相关…请参见以下示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <stdio.h>
template<int num>
void bar()
{
static int baz;
printf("bar<%i>::baz = %i
", 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;
} |
- +1用于keyword static unfortunately has a few different unrelated meanings in C++。
- +1完全同意
- 读完这本书,世界变得更有意义了,谢谢。
- 我喜欢模板的技巧。我迫不及待地想找个借口用它。
- 有没有人提到过"不鼓励使用未命名的名称空间"?
- @奥斯汀.马顿:"用静态表示‘本地到翻译单元’的短语在C++中被弃用。使用未命名的命名空间代替(82.5.1)在我的版本(第十打印,1999年9月)的C++编程语言上出现在第819页。
简化答案:
静态变量,不管它们是(非模板化的)class或(非模板化的)函数的成员,在技术上表现得像一个全局标签,其作用域仅限于class或函数。
- 不。全局在程序启动时初始化,函数静态在第一次使用时初始化。这是一个很大的不同。
- 我不认为这就是发生的事情。但是,这应该是特定于编译器的。
- 然后,您认为错误:3.61.在C++标准中规定,在静态启动时,命名空间范围的对象的构建发生在6.7;(4)一般规定"…这样的变量在控件第一次通过其声明时被初始化;这样的变量在初始化完成后被视为已初始化"。顺便说一句,这种首次使用时的初始化对于实现懒惰的单例构造非常方便。
- 3.7.4:"静态存储持续时间的块范围实体的常量初始化(3.6.2),如果适用,在首次输入其块之前执行。在允许实现静态初始化命名空间范围(3.6.2)中具有静态或线程存储持续时间的变量的相同条件下,允许实现对其他具有静态或线程存储持续时间的块范围变量进行早期初始化。否则,在控件第一次通过其声明时初始化该变量;
- 所以,我不认为我错了。我不知道我们是不是在谈论同一件事。在不可能进行常量初始化的情况下,您是对的。然后,"这样的变量在控件第一次通过其声明时初始化;"。
- 我不确定你是否真的不明白,或者你只是因为某种原因想战斗,我不明白。当然,如果您的程序不能判断初始化是否可以在之前完成,那么实现可以在之前自由地进行(但是,您又怎么能告诉它以前做过呢?)。如果在另一方面,您是唯一需要讨论的有趣案例,即,您可以分辨出不同之处,因为例如,静态是类的一个实例,而构造函数具有副作用,则必须在第一次输入块时进行。
- 除此之外,我什么都没说。我只是在说常量初始化。
- 不过,奇怪的是:1)对于常量初始化,在第一次进入块之前讨论本地静态是否可以初始化(变量只在块内可见,常量初始化不会产生副作用);2)关于常量初始化,您的帖子中没有提到任何内容;3)本地静态是v这对于像MyClass& instance(){ static MyClass x("config.ini"); return x; }这样的非常量初始化非常有用,这是一种单线程使用的有效的可移植实现,因为本地静态与全局静态不同,尽管您说过。