让我说我创造:
class Hello {
public:
int World(int in)
{
static int var = 0; // <<<< This thing here.
if (in >= 0) {
var = in;
} else {
cout << var << endl;
}
}
};
现在,如果我这样做:
Hello A;
Hello B;
A.World(10);
A.World(-1);
B.World(-1);
我输出“10”后跟另一个“10”。方法的 local 变量的值刚刚从一个类的实例跨越到另一个实例。
这并不奇怪 - 技术上方法只是具有隐藏this
参数的函数,因此静态局部变量的行为应该与常用函数一样。但它保证?它是由标准强制执行的行为,还是仅仅是编译器处理方法的快乐副产品?换句话说 - 这种行为是否可以安全使用? (...超出了让人不习惯的标准风险......)
答案 0 :(得分:3)
是。如果函数是一个类的[非静态]成员并不重要,那么它只保留一个实例的静态变量。
对这些变量的正确技术解释是那些变量是具有static duration
和internal linkage
的对象 - 因此这些名称会在程序退出之前存在,并且此名称的所有实例都指向同一实体。
答案 1 :(得分:1)
只需添加一个正确答案即可。如果您的类是模板化的,那么var
的实例只能在相同实例化类型的对象之间共享。所以如果你有:
template<typename C>
class Hello {
public:
int World(int in)
{
static int var = 0; // <<<< This thing here.
if (in >= 0) {
var = in;
} else {
cout << var << endl;
}
}
};
然后:
Hello<int> A;
Hello<int> B;
Hello<unsigned> C;
A.World(10);
A.World(-1);
B.World(-1);
C.World(-1);
然后最终输出将是“0”而不是“10”,因为Hello<unsigned>
实例化将拥有自己的var
副本。
答案 2 :(得分:0)
如果我们谈论的是Windows编译器,那么保证
https://msdn.microsoft.com/en-us/library/y5f6w579.aspx
以下示例显示了在成员函数中声明为static的局部变量。静态变量可用于整个程序;该类型的所有实例共享静态变量的相同副本。
他们使用的示例与您的示例非常相似。
我不知道GCC
答案 3 :(得分:0)
是的,保证。现在,回答问题&#34;在实例之间共享方法的本地静态变量的任何风险?&#34;它可能不那么直截了当。初始化和利用变量可能存在潜在风险,这些风险特定于方法的本地变量(而不是类变量)。
对于初始化,标准中的相关部分是6.7 / 4 [stmt.dcl]:
使用静态存储动态初始化块范围变量 执行持续时间(3.7.1)或线程存储持续时间(3.7.2) 第一次控制通过其声明;这样的变量是 在初始化完成时考虑初始化。如果 初始化通过抛出异常退出 是不完整的,所以下次控制时会再次尝试 进入宣言。如果控件同时进入声明 在初始化变量时,并发执行 应等待初始化完成。如果控制 在变量存在时递归地重新输入声明 初始化后,行为未定义。
在简单的情况下,一切都应该按预期工作。当变量的构造和初始化更复杂时,将存在特定于此情况的风险。例如,如果构造函数抛出,它将有机会在下一次调用时再次抛出。另一个例子是递归初始化,这是未定义的行为。
另一个可能的风险是该方法的性能。编译器需要实现一种机制来确保变量的兼容初始化。这是依赖于实现的,它很可能是一个锁,用于检查变量是否已初始化,并且每次调用该方法时都可以执行锁定。当发生这种情况时,它会对性能产生重大不利影响。