C++ 翻译单元

C++ Translation Unit

我不太明白什么是翻译单元以及如何使用未命名的命名空间:

如果我有 .cpp 文件:

namespace
{
    void extFunction()
    {
        std::cout << "Called Unnamed Namespace's function.\n";
    }
}

和一个主要的 .cpp 文件:

#include <iostream>
#include "ext.cpp"

using namespace std;

int main()
{
    extFunction();


    return 0;
}

为什么我可以从另一个文件访问未命名命名空间的成员?

编辑:

感谢您的回复;但是,我如何使用未命名的命名空间,目的是什么?

翻译单元基本上就是您交给编译器处理的代码块。编译器处理它并为 link er 生成 object 代码。 linker 将来自所有翻译单元的 object 代码组合成可执行文件。 (有时您会看到与此不同的细节,例如当您只有一个翻译单元时看不到 object 代码的文件。即使实现细节可能有所不同,这个概念仍然有效。)

因此,通常情况下,在编译和翻译单元时生成的 .o(或 .obj)个文件之间存在 one-to-one 对应关系。同样通常,每个 .cpp 文件会得到一个 .o 文件。因此,将每个 .cpp 文件视为其自己的翻译单元通常是合理的。直到你做一些非常规的事情。

当您使用 #include 指令时,您告诉编译器用包含文件的全部内容替换该行。也就是说,提供给编译器的代码块包括来自原始文件和包含文件的代码。如果将一个 .cpp 文件包含到另一个文件中,提供给编译器的代码块将包含来自两个 .cpp 文件的代码,从而打破 .cpp 文件和翻译单元之间的等价性。这通常被认为是一个坏主意。


让我们看一个例子。假设您有一个名为 ext.cpp 的文件,其中包含以下内容:

namespace
{
    void extFunction()
    {
        std::cout << "Called Unnamed Namespace's function.\n";
    }
}

还假设您有一个名为 main.cpp 的文件,其中包含以下内容:

#include <iostream>
#include "ext.cpp"


int main()
{
    extFunction();
    return 0;
}

如果您要编译 main.cpp,编译器要做的第一件事就是预处理 main.cpp。这会修改文件的内容,从而改变编译器看到的内容。预处理后,编译器将处理的代码块如下所示。

[lots of code from the library header named "iostream"]
namespace
{
    void extFunction()
    {
        std::cout << "Called Unnamed Namespace's function.\n";
    }
}


int main()
{
    extFunction();
    return 0;
}

此时,调用 extFunction 没有问题,因为编译器在它正在处理的代码块中看到未命名的命名空间。


关于使用未命名命名空间的请求信息的另一个示例。与上面类似,但又有所不同。假设您有一个名为 ext.cpp 的文件,其中包含以下内容:

#include <iostream>
namespace
{
    void extFunction()
    {
        std::cout << "Called Unnamed Namespace's function in EXT.\n";
    }
}

void extPublic()
{
    extFunction();
}

我们还提供一个 header (ext.h),它将声明具有外部 linkage.

的函数
void extPublic();

现在继续main.cpp

#include <iostream>
#include "ext.h"  // <-- Including the header, not the source.

namespace
{
    void extFunction()
    {
        std::cout << "Called Unnamed Namespace's function in MAIN.\n";
    }
}
int main()
{
    extFunction();
    extPublic();
    return 0;
}

看那个!名为 extFunction 的函数有两个定义! linker 不会感到困惑吗?一点也不。这些功能在其翻译单元之外是看不到的,因此没有冲突。如果将 main.cppext.cpp、link、main.oext.o 编译成单个可执行文件,则会得到以下输出。

Called Unnamed Namespace's function in MAIN.
Called Unnamed Namespace's function in EXT.

未命名命名空间的一个好处是您不必担心与另一个源文件的未命名命名空间中的名称发生冲突。 (当您的项目增长到包含数百个源文件时,这会带来更大的好处。)