Cannot create an instance of a class from another

2019-09-20 00:57发布

问题:

Okay, so this is really werid. I've never encountered anything like this.

Part of my program (Fails to compile) contains three namespaces as following:

// 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

Now, g++ went along just fine but the linker ld complains:

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

I have never ever encountered anything like this before... This just seems really weird, I don't even know any words/terms to describe this problem.

I would much appreciate any help.

Thanks,

回答1:

Your definitions of B::Tree::Tree() and B::Tree::~Tree() are declared inline. This means they are only available in that source file, not any others.

Either removing inline from the definitions, or moving the inline definitions into a header file included by all source files that need them, should fix the link errors.



回答2:

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

Those are not compiler errors, they are linker errors. The problem is that you declared the constructor and destructor, but never defined them. So the compiler finds the declarations and accepts them, but the linker cannot find the definitions to link the references to.

See this answer for what is a declaration and what is a definition and what they are good/needed for.



回答3:

You have to write the constructor and destructor for B::Tree somewhere either inline or in namespaceB.cc. By creating an instance of B, you are requiring the existence of the constructor and destructor.



回答4:

The pointer compiles fine because all pointers are the same. It's just the semantics allowed for different object types differ.
It would have compiled find up until the point you tried to use it.
But to create an actual object, you need the actual definition.

To use definitions from another namespace, you either used scopes, or use using:

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

or:

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


回答5:

You declare the Tree constructor & destructor, but you don't show us where you define them (you just say you implement Tree in namespaceB.cc)

Assuming you have defined them, you must ensure you are including both namespaceC.o and namespaceB.o on your link line.



回答6:

That's not a namespace problem, neither a compiler error. The compiler is happy, but linker fails to find the definition of B::Tree::Tree(). You need to supply the source file for implementation, or use -c flag to "just compile".



回答7:

First, you declared constructor and destructor of class Tree yourself so compiler didn't provide default implementation - you need to implement them yourself! (Or just remove those declarations...).

It is not enough to include headers but you need to explicitly say that you're using namespaces from those headers:

In namespaceC.cc add using namespace B; after you include namespace.B or, even better, prepend types with a namespace qualifier:

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


回答8:

I tried the codes in Visual studio 2005. If I remove the "inline" in namespaceB.cc for the constructor and destructor, it can link without errors.

Then I found this post: G++ won't accept inline constructors in C++

it says that for inline functions you must have its definition in every file that calls the function. So it is recommended to put inline definition in header file.