C++命名空间成员在不同文件中的访问如何?"namespace std"如何实施?

C++ Namespace member access in different files how to? how "namespace std" implemented?

本文关键字:namespace 何实施 std 访问 成员 命名空间 文件 C++      更新时间:2023-10-16

我已经在sample.h 中声明了以下命名空间

// namespace with identifier
namespace N1
{
    int b = 80;
}

sample1.cpp使用上面的命名空间声明

#include <iostream>
#include "sample.h"
using namespace std;
using namespace N1;
int main(void)
{
    cout << "b (in main) = " << b << endl;
      foo(); //written in sample2.cpp
      return 0;
}

sample2.cpp还使用sample.h 中声明的命名空间

#include <iostream>
#include "sample.h"
using namespace std;
using namespace N1;
void foo(void)
{
    cout << "b = " << b << endl;
}

当我编译时,我得到了以下错误

$> g++ sample1.cpp sample2.cpp
/tmp/ccB25lEF.o:(.data+0x0): multiple definition of `N1::b'
/tmp/cchLecEj.o:(.data+0x0): first defined here

让我知道如何解决以及如何实现"命名空间std"以避免此问题?

这不是#ifndef保护的问题。

使用头文件中的extern作为:

//sample.h
namespace N1
{
    extern int b; //extern is MUST!
    //DONT WRITE : extern int b = 80;
}

然后在.cpp文件中将其定义为:

//sample.cpp
namespace N1
{
    int b = 80;  //initialization should be here
}

Include guards仅在编译时工作,但错误发生在链接时。这是因为sample.h包含在这两个编译单元中,并且在这两个中都创建了变量N1::b
如果你真的想要一个变量(而不是const),你必须在头中声明它为extern,并在一个单独的编译单元中为它创建一个内存位置:

// sample.h
#ifndef N1
#define N1
namespace N1 {
    extern int b;
}
#endif
// sample.cpp
#include "sample.h"
namespace N1 {
    int b = 80;
}

这不是示例.h文件中缺少一次ifdef或pragma吗?

每次将sample.h#包含到.cpp模块中时,它都会为b创建一个新的链接器记录,从而在链接[1]上为您提供多个定义。

int应该在sample.cpp中定义,或者在其他地方定义,并在sample.h中简单地外部int b。


[1] 有些链接器会忽略这一点,你可以正常链接,但大多数时候它会产生错误。

您的问题是在头文件中定义了一个具有外部链接的对象,该头文件包含在两个独立的编译单元中。这个问题与名称空间本身无关。

一种解决方案是使头文件仅包含声明(例如,见下文),并将定义放在单个源文件上。

// sample.h
namespace N1
{
    extern int b;
}
// sample.cc
namespace N1
{
    int b = 80;
}

另一种解决方案是为对象提供内部链接,尽管这意味着您有多个名为b的对象,但这可能不是问题。例如,如果b假定为常量,那么这将起作用,因为默认情况下const对象具有内部链接。

// sample.h
namespace N1
{
    const int b = 80;
}

如果您只想定义一个常量,请尝试以下操作:

namespace N1
{
    enum MyEnum
    {
      b = 80
    };
}

对于几乎任何.h文件,包含保护程序都是个好主意,但它们可能不是你的问题。至关重要的一个定义规则有两个主要部分:第一部分说每个符号在每个翻译单元只能定义一次(通常意味着.cpp文件)。这就是include保护的作用:它们防止标头被包含两次,这将导致符号(如N::b)在同一转换单元(=.cpp文件)中被定义多次。

然而,这还不是全部。某些符号,如类、非内联函数和某些变量定义,对于整个程序只能声明一次。这并非没有道理:假设您允许在一个翻译单元中将一个名为MyInt的int值定义为40,在另一个转换单元中将其定义为80:编译器如何知道该使用哪一个?当然,您可以为每个程序多次声明此类符号(但每个翻译单元只能声明一次),或者它们只能在声明的翻译单元中使用。但您不能在多个翻译单元中定义它们。

使用enum是避免在您的情况下必须分离声明和定义的一种简单方法(因为enum不受第二版本的One definition规则的约束),但如果您真的需要int类型的(非常量)全局,则可以通过以下方式实现:

样本.h

namespace N1
{
    // The extern keyword tells the compiler that this is is only
    // a declaration and the variable is defined elsewhere.
    extern int b;
}

sample1.cpp

#include "sample.h"
namespace N1
{
    // This is the definition!
    int b = 5;
}
void foo()
{
    using namespace std;
    cout<<N1:b<<endl;
}

sample2.cpp

#include "sample.h"
// No need to define N1::b, since it was already defined in sample1.cpp.
void bar()
{
    using namespace std;
    cout<<N1:b<<endl;
}

程序包含变量N1::b的两个定义,而必须只有一个。变量必须在标头中用extern声明,并且只能在一个源文件中定义。