在不同翻译单元中定义的类[重复]

问题描述 投票:0回答:2

据我所知,一个类可以在多个翻译单元中定义,只要它们是相同的。考虑到这一点,请考虑以下示例:

 //1.cpp

class Foo{
 public:
  int i;
};


void FooBar();

void BarFoo(){
  Foo f;
}


int main(){
 FooBar();
 BarFoo();
}

//2.cpp

class Foo{
 public:
  std::string s;
};

void FooBar(){
  Foo f;
}

编译成功,我没有崩溃。

如果我进行以下更改:

//1.cpp
 Foo FooBar();
//2.cpp
 Foo FooBar(){
   Foo f;
   return f;
 }

我发生了车祸。为什么一个会导致崩溃,而另一个则不会。另外,我在第一个示例中是否违反了 ODR?如果是的话,为什么编译没问题?

c++
2个回答
2
投票

由于您所说的原因,该程序格式不正确。编译器不需要进行诊断,但我认为讨论格式错误的程序崩溃的原因没有意义。

尽管如此,我们还是这么做吧:

第一个示例可能不会崩溃,因为

FooBar
的行为不会影响
main
的运行。该方法被调用,它做了一些事情,就是这样。

在第二个示例中,您尝试返回

Foo
FooBar
返回
Foo
中定义的
2.cpp
的版本。
main
出现在
1.cpp
中,因此它需要
Foo
中定义的
1.cpp
版本,这是一个完全不同的版本 - 不同的成员、大小。您很可能会在析构函数上发生损坏。 (只是猜测)

编辑:这确实打破了单一定义规则:

3.2 一种定义规则[basic.def.odr]

6) 程序中可以有多个类类型的定义 [...],前提是每个定义 出现在不同的翻译单元中,并且定义满足以下要求。 [...]

  • D的每个定义应由相同的标记序列组成;

[...]


1
投票

以下是编译器/链接器的工作原理:

  1. 编译器翻译具有提供的标头的 cpp 文件。它生成一个 .obj 文件。在您的情况下,o.bj 文件将引用数据结构

    Foo
    。并且不会有任何其他细节。

  2. 链接器将 .obj 文件链接在一起。它仅比较字符串名称。在你的 obj 文件中你有相同的

    Foo
    。名字相符。对于链接器来说这是同样的事情。

  3. 之后您就可以开始您的程序了。最有可能的是它会崩溃。更具体地说,它将显示未定义的行为。它可以进入无限循环,显示奇怪的消息等等

您有责任为每个 cpp 文件的翻译提供 cpp 文件中相同的标头或定义。现有的软件工具无法为您检查这一点。这就是它的工作原理。

© www.soinside.com 2019 - 2024. All rights reserved.