Не удается создать экземпляр класса из другого пространства имен? - PullRequest
0 голосов
/ 23 февраля 2012

Хорошо, так что это действительно странно. Я никогда не встречал ничего подобного.

Часть моей программы (не компилируется) содержит три пространства имен:

// 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()'

Я никогда раньше не сталкивался с чем-то подобным ... Это кажется странным, я даже не знаю слов / терминов для описания этой проблемы.

Я был бы очень признателен за любую помощь.

Спасибо

Ответы [ 8 ]

3 голосов
/ 23 февраля 2012
 namespaceC.cc: undefined reference to `B::Tree::Tree()'
 namespaceC.cc: undefined reference to `B::Tree::~Tree()'

Это , а не ошибки компилятора , это ошибки компоновщика . Проблема в том, что вы объявили конструктором и деструктором, но никогда не определили их. Таким образом, компилятор находит объявления и принимает их, но компоновщик не может найти определения для ссылки на ссылки.

См. этот ответ о том, что такое декларация и что такое определение и для чего они хороши / необходимы.

2 голосов
/ 23 февраля 2012

Ваши определения B::Tree::Tree() и B::Tree::~Tree() объявлены как встроенные. Это означает, что они доступны только в этом исходном файле, а не в других.

Либо удаление inline из определений, либо перемещение встроенных определений в заголовочный файл, включаемый во все исходные файлы, которые в них нуждаются, должно исправить ошибки ссылки.

1 голос
/ 23 февраля 2012

Вы должны написать конструктор и деструктор для B::Tree где-нибудь либо встроенным, либо в namespaceB.cc .Создавая экземпляр B, вы требуете наличия конструктора и деструктора.

0 голосов
/ 23 февраля 2012

Я пробовал коды в Visual studio 2005. Если я удаляю «inline» в namespaceB.cc для конструктора и деструктора, он может связываться без ошибок.

Тогда я обнаружил этот пост: G ++ не будет принимать встроенные конструкторы в C ++

, там говорится, что для встроенных функций вы должны иметь свое определение в каждом файле, который вызываетфункция.Поэтому рекомендуется поместить встроенное определение в заголовочный файл.

0 голосов
/ 23 февраля 2012

Во-первых, вы сами объявили конструктор и деструктор класса Tree, поэтому компилятор не предоставил реализацию по умолчанию - вам нужно реализовать их самостоятельно! (Или просто удалите эти объявления ...).

Недостаточно включить заголовки, но вам нужно явно сказать, что вы используете пространства имен из этих заголовков:

В namespaceC.cc добавьте using namespace B; после включения namespace.B или, что еще лучше, добавьте типы с префиксом пространства имен:

B::Tree *tree;    
B::Tree tree;     
0 голосов
/ 23 февраля 2012

Это не проблема namespace , ни ошибка компилятора. Компилятор доволен, но компоновщик не может найти определение B::Tree::Tree(). Вам необходимо предоставить исходный файл для реализации или использовать флаг -c, чтобы "просто скомпилировать".

0 голосов
/ 23 февраля 2012

Вы объявляете конструктор и деструктор Tree, но не показываете нам, где вы их определяете (вы просто говорите, что реализуете Tree в namespaceB.cc)

Предполагая, что вы определили их, вы должны убедиться, что вы включили оба namespaceC.o и namespaceB.o в вашей строке ссылки.

0 голосов
/ 23 февраля 2012

Указатель компилируется нормально, потому что все указатели одинаковы.Это просто семантика, разрешенная для разных типов объектов, различается.
Это скомпилировало бы поиск до точки, в которой вы пытались его использовать.
Но чтобы создать реальный объект, вам нужно фактическое определение.

Чтобы использовать определения из другого пространства имен, вы либо используете области, либо используете , используя :

#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!?
    }
    }
...