Неопределенная ссылка на функцию-член - PullRequest
2 голосов
/ 26 августа 2011

Я пытаюсь решить проблему компиляции, связанную с внешним определенным конструктором.

Вот 2 класса, у которых есть эта проблема:

//Username.h

#ifndef USERNAME_H
#define USERNAME_H

#include <string>
using namespace std;

class Username{
private:
    string Name;
public:
    Username(string = "");
    string getName() const;
    void setName(string);
};

#endif

...

//Username.cpp

#include "Username.h"

Username::Username(string n):Name(n){}
string Username::getName() const{return Name;}
void Username::setName(string n){Name = n;}

...

//User.h

#ifndef USER_H
#define USER_H

#include <string>
#include "Username.h"
using namespace std;

class User{
protected:
        Username* UserUsername;
public:
    User(string s);
    virtual ~User();
    Username* getUsername() const;
    void setUsername(Username*);
};

#endif

...

//User.cpp

#include "User.h"

User::User(string s):UserUsername(new Username(s)){}

User::~User(){}

Username* User::getUsername() const{return UserUsername;}

void User::setUsername(Username* u){UserUsername=u;}

int main(){return 0;}

Если я скомпилирую, используя "g ++ User.cpp", я получаю эту ошибку:

/tmp/ccEmWmfl.o: In function `User::User(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':

User.cpp:(.text+0x3e): undefined reference to `Username::Username(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'

collect2: ld returned 1 exit status

Если я использую «g ++ User.cpp Username.cpp -o main» или если я использую встроенные конструкторы / деструкторы, я не получаю ошибку.

Эти классы очень просты, но у меня есть тонны для компиляции, которые требуют более одного класса.

Я новичок в компиляции в оболочке Ubuntu с g ++, поэтому, пожалуйста, кто-нибудь может мне помочь понять?

Ответы [ 7 ]

3 голосов
/ 26 августа 2011

g ++ User.cpp

Компилирует User.cpp и пытается создать из него исполняемый файл (т. Е. Вызывается компоновщик). Поскольку User.cpp имеет символ, не полностью определенный в User.cpp (конструктор имени пользователя здесь:

User::User(string s):UserUsername(new Username(s)){}

ожидается, что символ будет определен на этапе соединения. Связывание выполняется путем объединения всех выходных данных сгенерированного объектного файла всех созданных вами cpps и объединения недостающих символов. Здесь вы не говорите g ++ о том, где найти полное определение конструктора имени пользователя, кроме cpp с main, так что это не удалось.

Это, однако:

g ++ User.cpp Username.cpp -o main

Сообщает компоновщику, где искать полные определения имени пользователя (в объектном файле, созданном путем компиляции Username.cpp). Таким образом, связывание может успешно заполнить недостающие фрагменты, используя то, что определено в Username.cpp, чтобы сопоставить идентификаторы, не определенные в User.cpp.

Вы можете подумать - «ну, я сказал об этом компилятору, включив заголовочный файл, он должен знать!». То, что вы сделали, это объявленный символ. Вы пообещали, что что-то в конечном итоге будет определено, либо во время компиляции этого одного cpp, либо позже связав его с другим объектным файлом, сгенерированным путем компиляции другого cpp. g ++ должен знать, откуда вы собираетесь извлекать все ваши определения, чтобы он мог правильно построить конечный исполняемый файл.

3 голосов
/ 26 августа 2011

Вы уже ответили на свой вопрос, если вы не добавите Username.cpp в процессе компиляции, пользователь не может его знать.

2 голосов
/ 26 августа 2011

Вы можете использовать частичную компиляцию с флагом -c:

g++ -c User.cpp

Это создает файл User.o.

Вы делаете эту частичную компиляцию для каждого из ваших файлов

g++ -c Username.cpp

А затем вы связываете все объектные файлы (файлы * .o) вместе:

g++ User.o Username.o -o main

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

1 голос
/ 05 декабря 2013

Это также может произойти, если вы пытаетесь вызвать функцию-член класса шаблона из исходного файла, отличного от файла реализации класса шаблона.Например: у вас есть класс ABC с ABC.h и ABC.cpp (файл реализации).

Пока ваши вызовы универсальных объектов находятся в ABC.cpp, все работает хорошо.Однако, если вы попытаетесь включить ABC.h в другой класс, скажем, BCD, и вызовете любую функцию-член объекта ABC, вы получите сообщение об ошибке неопределенной ссылки на член.Шаблонные классы, включая реализацию, чтобы избежать этой ошибки, которая вводит в заблуждение.

0 голосов
/ 26 августа 2011

Вы должны передать все свои файлы реализации в g++ исполняемый файл. Первая попытка компиляции не удалась, потому что вы только компилируете User.cpp в объектный файл и связываете его в исполняемый объект с именем main.

Во второй попытке вы правильно передаете оба необходимых файла реализации в исполняемый файл g++. В этом случае User.cpp и Username.cpp компилируются в объектные файлы и связываются вместе, образуя исполняемый файл main. В этом случае присутствует реализация конструктора Username::Username().

0 голосов
/ 26 августа 2011

Это не проблема C ++, а проблема GCC. Обычно вы создаете большую программу с Make. Затем у вас есть правило make-файла, которое собирает все файлы .cpp в каталог и связывает их вместе. Альтернативой является то, что ваш make-файл специально указывает, какие файлы .cpp должны быть скомпилированы вместе.

0 голосов
/ 26 августа 2011

User.cpp добавить:

#include "Username.h" 
...