提问人:YoonSeok OH 提问时间:4/30/2023 最后编辑:YoonSeok OH 更新时间:5/1/2023 访问量:375
静态局部变量如何沿不同的翻译单元共享?
How can static local variable shared along different translation unit?
问:
静态局部变量如何沿不同的翻译单元共享?
我知道“静态”指定内部链接。
如果“静态”成员函数在不同的转换单元中具有相同的地址,则每个转换单元都有该函数的副本,链接器选取要包含在可执行文件中的副本之一。
然后,在两个不同翻译包含 1 个共享头的场景中,该头有一个带有静态成员函数的类,该函数递增静态局部变量,并且每个翻译单元 (cpp) 调用共享头的静态成员函数,每个翻译单元都会递增自己独立的静态局部变量,因为 static 指定了内部链接。例如
共享.h
#pragma once
class CShared
{
public:
static inline void Foo()
{
static int s_nCount = 0;
++s_nCount;
}
private:
CShared(){}
~CShared(){}
};
啊
#pragma once
class A
{
public:
A(){}
~A(){}
void Foo();
};
.cpp
#include "A.h"
#include "Shared.h"
void A::Foo()
{
CShared::Foo();
}
B.h
#pragma once
class B
{
public:
B(){}
~B(){}
void Foo();
};
B.cpp
#include "B.h"
#include "Shared.h"
void B::Foo()
{
CShared::Foo();
}
main.cpp
#include <iostream>
#include "A.h"
#include "B.h"
int main()
{
A a;
B b;
a.Foo();
b.Foo();
return 0;
}
结果是 s_nCount 当 b.Foo() 变为 2。因此,静态局部变量是“共享的”,而不是每个翻译单元都有自己的静态局部变量副本,沿着不同的翻译单元。这难道不意味着静态局部变量不是内部链接吗? “不同翻译单元之间共享”和“内部链接”不是冲突的术语吗?
任何澄清这一点的人都会受到真正的赞赏。
[更新]
到目前为止,似乎,
静态的 | 联动 |
---|---|
CShared::Foo | 内部链接以外的东西 |
s_nCount | 外部链接(Mike Nakis,The Dreams Wind)或无链接(来自莫斯科的Vlad) |
根据一些程序员的家伙的说法,
静态的 | 联动 |
---|---|
CShared::Foo | 无链接 (cppreference) |
s_nCount | 无链接 (cppreference) |
在我的理解中, CShared::Foo 属于“无链接 - 本地类及其成员函数” s_nCount属于“无链接 - 未显式声明为 extern 的变量(无论 static 修饰符如何)”
谁能得出这个结论?
[更新2]
静态的 | 联动 |
---|---|
CShared::Foo | 外部链接 |
s_nCount | 无联动 |
关于成员函数前面的“静态”含义
关于“全局”变量前面的“静态”含义
关于“局部”变量前面的“静态”含义
答:
我认为您可能会对全球范围感到困惑。 在 .static
static
class
事实上,您链接到的 wikibooks.org 页面就是这样说的。我引用:
这导致“static”关键字仅在实现文件中使用,从不在头文件中使用,除非在头文件的类定义中使用“static”,其中它表示内部链接以外的其他内容。
因此,如果您只是在全局范围内拥有它:;然后会有内部联动。static int a = 0
a
但是,如果你有,那么它只是一个类变量(即,不是一个实例变量),并且它具有外部链接。class A { static int a = 0; }
a
评论
在此成员函数声明中
static inline void Foo()
{
static int s_nCount = 0;
++s_nCount;
}
局部变量没有内部链接。它不是命名空间变量。它是一个声明的局部变量,没有存储类说明符。它是一个局部变量,具有静态存储持续时间,没有链接。因此,该函数只有一个局部变量,该变量仅初始化一次,并在函数的每次调用中更改。s_nCount
extern
来自 C++17 标准(6.5 程序和链接)
3 具有命名空间范围 (6.3.6) 的名称具有内部链接,如果它 是...
8 本规则未涵盖的名称没有关联。此外,除了 如前所述,在块范围 (6.3.3) 声明的名称没有链接。
和(10.1.6 内联说明符)
- ....[ 注意:具有外部链接的内联函数中的静态局部变量始终引用同一对象。定义的类型 在具有外部链接的内联函数的主体中是 每个翻译单元中的类型相同。— 尾注 ]
下面是在块作用域中声明的具有内部链接的变量的示例。
#include <iostream>
static int n;
void f()
{
extern int n;
++n;
}
int main()
{
for ( size_t i = 0; i < 5; i++ )
{
std::cout << ( f(), n ) << ' ';
}
std::cout << '\n';
}
程序输出为
1 2 3 4 5
因此,在全局命名空间中声明的带有存储类说明符的变量具有内部链接。在函数的块作用域中声明的局部变量表示(引用)在全局命名空间中声明的具有内部链接的变量。n
static
n
f
n
评论
static
在不同的上下文中应用关键字时,可以具有不同的含义。 局部变量旁边的关键字引入了静态存储持续时间,但不会引入内部链接(就像关键字对全局变量所做的那样)。static
只要声明局部变量的函数具有外部链接(成员函数是外部链接),所有转换单元都共享相同的局部变量。如果函数具有内部链接,则所有转换单元都将具有自己的局部变量实例(例如,如果您在类声明之外定义了它):static
static
static
static
#pragma once
// Defined in the same header outside of the class body
static void Foo()
{
static int s_nCount = 0;
++s_nCount;
}
class CShared
{
// ..Class definition goes here.. //
}
....
评论
static
A.h
您的问题中缺少标题。static
修饰符如何);”(强调我的)因此,在块中声明的变量是否具有并不重要,只要它没有被声明,因为它没有链接。extern
static
extern