无法从另一个命名空间创建类的实例?

Posted

技术标签:

【中文标题】无法从另一个命名空间创建类的实例?【英文标题】:Cannot create an instance of a class from another namespace? 【发布时间】:2012-02-23 14:07:03 【问题描述】:

好的,这真的很奇怪。我从来没有遇到过这样的事情。

我的部分程序(编译失败)包含三个命名空间,如下:

// namespaceA.h
namespace A 
enum Kind  jimmy, david ;

// end of namespaceA.h

// namespaceB.h
#include "namespaceA.h"
namespace B 
class Tree 
    public:
    Tree *prev;
    Tree *next;
    Tree *down;
    A::Kind kind;

    Tree();
    ~Tree();
;

// end of namespaceB.h
// Implementation details of the class are placed in namespaceB.cc
// Constructor / Desctructor defined in the namespaceB.cc file!
// Something like this,
#include "namespaceB.h"
namespace B 
inline Tree::Tree()  ... 
inline Tree::~Tree()  ... 


// namespaceC.cc
#include "namespace.B"
namespace C 
void run() 
    B::Tree *tree;    // FINE
    B::Tree tree;     // Fail to compile!?


// end of namespaceC.cc

现在,g++ 运行良好,但链接器 ld 抱怨:

 "namespaceC.cc: undefined reference to `B::Tree::Tree()'
 "namespaceC.cc: undefined reference to `B::Tree::~Tree()'

我以前从未遇到过这样的事情......这看起来真的很奇怪,我什至不知道任何词语/术语来描述这个问题。

我将非常感谢任何帮助。

谢谢,

【问题讨论】:

你如何尝试链接它?您似乎忘记链接到定义 B 的文件。 我以一种非常愚蠢的方式编译整个程序。我只是“g++ -Wall *”它。也许这是个问题? 应该可以。我记得内联构造函数有问题,也许你可以尝试删除 B::Tree() 和 Tree::~Tree() 前面的内联 是的!那确实解决了! *** 上的你们太有帮助了!非常感谢 【参考方案1】:
 namespaceC.cc: undefined reference to `B::Tree::Tree()'
 namespaceC.cc: undefined reference to `B::Tree::~Tree()'

这些不是编译器错误,它们是链接器错误。问题是您声明了构造函数和析构函数,但从未定义它们。所以编译器找到声明并接受它们,但是链接器找不到定义来链接引用。

请参阅 this answer,了解什么是声明、什么是定义以及它们的优点/需要。

【讨论】:

我确实在 namespaceB.cc 文件中定义了构造函数和析构函数。我将编辑我的问题以使其更清楚 @BeyondSora:那么您要么没有编译 namespacB.cc 文件,要么没有将生成的目标文件传递给链接器。因为错误消息明确指出链接器找不到这些定义。【参考方案2】:

您对B::Tree::Tree()B::Tree::~Tree() 的定义是内联声明的。这意味着它们仅在该源文件中可用,在任何其他文件中均不可用。

无论是从定义中删除inline,还是将内联定义移动到需要它们的所有源文件包含的头文件中,都应该修复链接错误。

【讨论】:

你先生是男人!!!非常感谢你。在这里,我认为内联会提高效率等。我为了这件事熬夜到今天早上 4 点。非常感谢你!【参考方案3】:

您必须在内联或 namespaceB.cc 中的某处为 B::Tree 编写构造函数和析构函数。通过创建B 的实例,您需要存在构造函数和析构函数。

【讨论】:

是的,我在 namespaceB.cc 中使用了类似“inline Tree::Tree() ... ...”这样的东西【参考方案4】:

指针编译得很好,因为所有指针都是相同的。只是不同对象类型所允许的语义不同。 它会一直编译 find 直到您尝试使用它。 但是要创建一个实际的对象,您需要实际的定义。

要使用来自另一个命名空间的定义,您可以使用范围,或者使用 using

#include "namespace.B"
using namespace B;
namespace C 
void run() 
    Tree *tree;    // FINE
    Tree tree;     // Fail to compile!?


或:

   #include "namespace.B"
    namespace C 
    void run() 
        B::Tree *tree;    // FINE
        B::Tree tree;     // Fail to compile!?
    
    

【讨论】:

【参考方案5】:

你声明了 Tree 构造函数和析构函数,但你没有告诉我们你在哪里定义它们(你只是说你在 namespaceB.cc 中实现了 Tree)

假设您已经定义了它们,您必须确保在链接行中同时包含 namespaceC.o namespaceB.o。

【讨论】:

【参考方案6】:

这不是 命名空间 问题,也不是编译器错误。编译器很高兴,但链接器找不到B::Tree::Tree() 的定义。你需要提供源文件来实现,或者使用-c标志来“编译”。

【讨论】:

【参考方案7】:

首先,您自己声明了类Tree 的构造函数和析构函数,因此编译器没有提供默认实现——您需要自己实现它们! (或者只是删除那些声明...)。

仅包含标头是不够的,但您需要明确说明您正在使用来自这些标头的命名空间:

namespaceC.cc 中,在包含namespace.B 之后添加using namespace B;,或者更好的是,在类型前面加上命名空间限定符:

B::Tree *tree;    
B::Tree tree;     

【讨论】:

对不起,我的实际程序中确实有命名空间...我现在将编辑我的问题以更正它。【参考方案8】:

我尝试了 Visual Studio 2005 中的代码。如果我删除 namespaceB.cc 中构造函数和析构函数的“内联”,它可以无错误地链接。

然后我发现了这个帖子:G++ won't accept inline constructors in C++

它说对于内联函数,您必须在每个调用该函数的文件中都有它的定义。所以建议将内联定义放在头文件中。

【讨论】:

以上是关于无法从另一个命名空间创建类的实例?的主要内容,如果未能解决你的问题,请参考以下文章

类命名空间与对象实例的命名空间

类命名空间与对象实例的命名空间和下面向对象的组合用法

Python()-类命名空间和对象/实例命名空间

为啥我不能创建在与类定义相同的命名空间中定义的 C++ 类的实例?

类命名空间与对象实例的命名空间 and 面向对象的组合用法

20180724 (面向对象:类的命名空间和查询顺序丶组合)