-2

我有一个来自源文件 s.cpp 的结构:

struct s{
unsigned long long a;
s(unsigned long long b){a=b;}
unsigned long long get(){return a;}
};

在主文件中,当然用 g++ main.cpp s.cpp 简要语法编译

struct s{
s(unsigned long long b);
unsigned long long get();
};

s s1(123456);
//some code
unsigned long long c=s1.get();

现在我知道它不会编译返回未定义的引用,这是一种耻辱。必须在 s.cpp 中的括号外定义 ctor 和 get()。我想知道是否有可能不需要的 g++ 标志。但主要问题是, s1.get() 是安全的还是未定义的行为?

编辑

感谢您的回复,但我仍然不太了解机制。所以在这种情况下,在 s.cpp 中:

struct teste{
    int a=0;
    teste(int b);
    int g();
};
teste::teste(int b){a+=b;}
int teste::g(){ return a;}

主要是:

struct teste{
    int c=1;
    teste(int b);
    int g();
};

teste ato(8);
printf("\n%d",ato.g());

考虑到内存分配,不应该在实际执行时返回 9 而不是 8?即使我将 c 更改为 a,使其与 1 完全相同,它似乎总是从 s.cpp 中查找 a 及其初始值。建议向 main 声明除方法之外的其他东西是多余的。

4

5 回答 5

2

不,这不安全——如果你能设法让它编译,这是未定义的行为。它直接违反了One Definition Rule,特别是 C++03 §3.2/5,它说:

如果每个定义出现在不同的翻译单元中,并且定义满足以下要求,则程序中可以有多个类类型的定义(第 9 条)[...]。给定这样一个D在多个翻译单元中定义的实体,那么
- 每个定义D都应由相同的标记序列组成;和
...省略更多条件...

[...] 如果 的定义D满足所有这些要求,那么程序的行为就好像有一个D. 如果 D 的定义不满足这些要求,则行为未定义。

由于您的两个类定义包含相同的标记序列,因此行为未定义。

C++11 的行为在这里是相同的。

于 2013-05-16T17:19:00.990 回答
2

你有一个奇怪的声明和定义组合。

首先,看起来你的类声明(你应该把它放在一个头文件中,包含在 中main)应该是这样的:

struct s
{
  unsigned long long a;
  s(unsigned long long b);
  unsigned long long get();
};

然后,您的实现应该类似于

s::s(unsigned long long b) a(b) {}

unsigned long long  s::get() { return a; }
于 2013-05-16T17:02:40.713 回答
1

如果我理解正确,那么与将声明放在头文件中相比,将声明和定义放在一个文件中并将不同的声明放在不同的翻译单元中是否合法。

当且仅当同一类型的所有声明在所有翻译单元中完全相同*,那么它是合法的。但这很容易出错,如果您修改其中一个声明,或者任何其他更改影响了确切的定义,您将破坏 ODR 并导致未定义的行为。

*:不仅声明必须在文本上相同,而且对每个符号的查找都必须产生相同的实体,并且基本上它们必须真正指代一个且只有一件事。

于 2013-05-16T17:21:03.363 回答
0

您正在混合两个具有相同名称的不同结构,可能在同一个命名空间中,这可能会导致问题。

在 s.cpp 中(我稍微修改了代码以便于理解):

struct s{ //I'll refer to this struct as S1 below
    int _a;
    s(int a) : _a(a) {}
    int get() {return _a;}
};

在 main.cpp

// you added this struct declaration because otherwise it desn't compile
// (providing there is no silly #include "s.cpp")
// But it actually declares ANOTHER struct called s as well
struct s{ // I will refer to this struct as S2 below
    s(int a);
    int get();
};

s my_s(123);
//some code
int c = my_s.get(); // the compiler checks that S2 contains a get function: OK

现在,发生的事情是链接器将s在同一个命名空间中看到两个符号,并且这里会出现一些调试起来非常烦人的问题。您可能会123恢复您的价值,一切似乎都可以正常工作,但是您在发布时编译(或更改编译器,或更改平台,或重新启动您的机器,或任何其他您认为不应该改变代码行为的事情) 而且它不再起作用了...

为避免此类问题,当您要使用结构时,需要在使用它的行之前声明它。在 sh 中声明它并在 s.cpp 中定义它(我猜这是您尝试在 .cpp 文件中执行的操作,但您需要分别定义每个函数):

// s.h
struct s{
    unsigned long long a;

    s(unsigned long long b);
    unsigned long long get();
};

// s.cpp
s::s(unsigned long long b) : a(b) {}

unsigned long long s::get() { return a; }

或者在你的 .h 文件中内联所有内容并将其包含在你的 main.cpp 中。

于 2013-05-16T17:08:20.307 回答
0

这里的问题是s.cpps中隐式定义的成员函数。inline这将这些成员函数限制为仅在 s.cpp 的翻译单元中定义。在 main.cpp 中,编译器只能看到声明。因此链接器错误。(我相信这被称为具有“内部联系”或类似的东西,如果有人可以验证?)。

如前所述,将函数的定义移到外部可以s解决编译器错误。原因是定义不再inline。因此,该定义现在可以链接到 main.cpp 并在其中使用。所以新代码是定义良好的c++。

关于inline函数的问题是它们必须在使用它们的每个翻译单元中定义。出于这个原因,inline函数通常放在头文件中,这样一个简单的#include就足够了。

于 2013-05-16T17:08:00.727 回答