Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Undefined Reference To Member function

Tags:

c++

g++

I'm trying to solve a compiling problem regarding outside defined constructor.

Here's 2 of the classes that have this problem:

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

If I compile using "g++ User.cpp" I get this error:

/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

If I use "g++ User.cpp Username.cpp -o main" or if I use inline constructors/destructors I get no error.

These classes are very easy, but I have tons more to compile that require more than one class.

I'm new at compiling in Ubuntu shell with g++, so please, can someone help me to understand?

like image 245
DkSw Avatar asked Aug 26 '11 14:08

DkSw


3 Answers

g++ User.cpp

This compiles User.cpp and attempts to create an executable from it (ie the linker is invoked). Since User.cpp has a symbol not fully defined in User.cpp (the Username constructor here:

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

the symbol is expected to be defined at the link stage. Linking is done by combining all the generated object file output of all the cpps you generated and piecing together the missing symbols. Here you're not telling g++ about where to find the full definition of the Username constructor other than the cpp with main, so its failing.

This, however:

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

Tells the linker where to find full Username definitions (in the object file generated by compiling Username.cpp). So linking can succeed in filling in the missing pieces by using whats defined in Username.cpp to match up identifiers not defined in User.cpp.

You may think -- "well I've told the compiler about it by including the header file, it should know!". What you've done is declared the symbol. You've made a promise that something will eventually be defined, either during compilation of that one cpp or by later linking it with another object file generated by compiling another cpp. g++ needs to know where you intend to pull all your definitions from so it can build a final executable correctly.

like image 77
Doug T. Avatar answered Nov 15 '22 09:11

Doug T.


You already answered your question, if you don't add Username.cpp at the compilation process User can't knwo it.

like image 22
Whiskas Avatar answered Nov 15 '22 07:11

Whiskas


You can use partial compilation, with the -c flag:

g++ -c User.cpp

This produces a User.o file.

You do this partial compilation thing on each of your files

g++ -c Username.cpp

And then you link all the object files (the *.o files) together:

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

Usually, you'd use some build system to automate this process. With that you'd also get other advantages, like not skipping the recompilation of files that didn't change since the last compilation.

like image 42
R. Martinho Fernandes Avatar answered Nov 15 '22 07:11

R. Martinho Fernandes