5

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?

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
DkSw
  • 101
  • 1
  • 4
  • 12

7 Answers7

4

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.

Doug T.
  • 64,223
  • 27
  • 138
  • 202
3

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

Whiskas
  • 352
  • 1
  • 2
2

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.

Community
  • 1
  • 1
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
1

This can also happen, if you are trying to call a member function of a template class from the source file other than the implementation file of the template class. For example: you have clas ABC with ABC.h and ABC.cpp (implementation file).

As long as your calls to generic objects lies in ABC.cpp, all works well. However, if you try to include ABC.h in another class say BCD and call any member function of object ABC, you will get the Undefined Reference To Member function error.

Solution: Create a single header file for your template classes including the implementation to avoid this error which is misleading.

0

User.cpp add:

#include "Username.h" 
Jiri Kriz
  • 9,192
  • 3
  • 29
  • 36
0

It's not really a C++ problem, but more a GCC issue. Normally, you'd build a larger program with Make. You then have a makefile rule that builds all .cpp files in a directory and links hem together. An alternative is that your makefile specifically states which .cpp files should be compiled together.

MSalters
  • 173,980
  • 10
  • 155
  • 350
0

You have to pass all of your implementation files to the g++ executable. The first compilation attempt fails because you're only compiling User.cpp into an object file and linking it into an executable object called main.

In the second attempt you correctly pass both necessary implementation files to the g++ executable. In this case, User.cpp and Username.cpp are compiled into object files and linked together to form the main executable. In this case, the implementation of the Username::Username() constructor is present.

Chad
  • 18,706
  • 4
  • 46
  • 63