静态初始化混淆

时间:2010-06-14 10:27:00

标签: c++ static initialization

我对c ++中的一些概念感到非常困惑。例如:我有以下两个文件

//file1.cpp
class test
{
    static int s;
    public:
    test(){s++;}
};

static test t;
int test::s=5;

//file2.cpp
#include<iostream>
using namespace std;
class test
{
    static int s;
    public:
    test(){s++;}
    static int get()
    {
    return s;
    }
};

static test t;

int main()
{
    cout<<test::get()<<endl;
}

现在我的问题是:
1.即使两个文件具有不同的类定义,两个文件如何成功链接? 2.两个类的静态成员是否相关,因为我输出为7.

请解释这个静力学的概念。

5 个答案:

答案 0 :(得分:3)

它们之间的链接是因为链接器几乎不了解C ++语言。但是,如果执行此操作,则会破坏单一定义规则,并且程序的行为将是未定义的。编写无效代码不是学习C ++的好方法。另外,你似乎对静态变量有很多疑问 - 这个概念真的不是那么复杂 - 你使用哪种C ++教科书并不能很好地解释它?

答案 1 :(得分:1)

类(就链接器而言)是相同的。 get()只是链接器永远不会看到的内联函数。

类中的static不会将成员限制为文件范围,但会使其对所有类实例都是全局的。

[edit:]如果您将int test::s=5;放入第二个文件,则会出现链接器错误。

答案 2 :(得分:1)

静态是C ++中的一种奇怪的野兽,它具有不同的含义,具体取决于上下文。

在File1.cpp中,类定义中的“static int s”表示s是静态成员,即对所有测试实例都是通用的。

“静态测试t”具有不同的含义:静态全局变量仅存在于编译单元中,并且其他单元不可见。这是为了避免链接器混淆,如果你使用相同的名称两个不同的东西。在现代C ++中,人们会使用匿名命名空间:

namespace
{
  test t;
}

这意味着File1.cpp中的t和File2.cpp中的t是单独的对象。

在File2.cpp中,您还定义了静态方法get:静态方法是属于类而不是实例的方法,并且只能访问类的静态成员。

您在示例中未使用的静态的最后一个用法是本地静态变量。本地静态变量在第一次执行时初始化,并在整个执行过程中保持其值,有点像具有本地范围的全局变量:

int add()
{
  static value = 0;
  value++;
  return value;
}

重复调用add()将返回1,然后是2,然后是3 ......例如,本地静态构造对本地缓存很有用。

答案 3 :(得分:0)

  
      
  1. 两个文件如何成功链接,即使它们有不同的类定义?
  2.   

您违反了一个定义规则(ODR),方法是在不同的翻译单元中以不同方式定义相同的类。这会调用可怕的 Undefined Behavior 。这意味着代码可以执行任何操作,包括但不限于执行您想要的操作,格式化硬盘或导致无法解决太阳日蚀问题。
请注意,不要求编译器/链接器检测ODR违规。

  
      
  1. 两个类的静态成员是否相关,因为我输出为7。
  2.   

也许他们是,也许他们不是。实际上,未定义的行为可能会做任何事情。你看到的只是你的编译器和链接器实现的工件 可能的实现将愉快地将对test::s的所有引用链接到同一实例,但让每个翻译单元具有 - 并链接到 - 他们自己的t对象。 (由于该类只有inline个函数,因此除了test和那些test::s实例之外,链接器很可能甚至看不到t的任何内容。)

答案 4 :(得分:0)

解决C ++实际工作方式的副作用。使用命名空间使类名与外部不同。

过去我曾经在实践中使用了相当繁琐的规则:每个库和文件都必须有一个私有命名空间,以避免这种链接冲突。或者将实用程序类放入主类定义中。无论如何:不要污染全局命名空间,最重要的是确保整个项目中名称的唯一性。 (写一下,现在我注意到我几乎逐字地使用了Java的概念。)

我在C ++中搜索相当于C的静态是没有用的......