2011-08-26 135 views
1

我试图解决有关外部定义的构造函数的编译问题。未定义的成员函数参考

下面是有这个问题的类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主”,或者如果我使用内联构造函数/析构函数我没有得到任何错误。

这些类很容易,但我有更多的编译需要多个类。

我是用g ++在Ubuntu shell编译的新手,所以请有人帮我理解?

+2

请注意,在头文件中使用'namespace XXX'被认为是非常糟糕的做法。 –

+0

所以很糟糕的设计代码。 – Nawaz

+1

为什么不使用Makefile或者更方便的[build system](http://www.cmake.org/)? –

回答

3

g++ User.cpp

这编译User.cpp并尝试从它创建一个可执行文件(即链接器被调用)。由于User.cpp在User.cpp没有完全定义的符号(用户名构造这里:

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

符号,预计在链路阶段被定义链接通过将所有生成的目标文件输出完成在这里你不告诉g ++关于在哪里找到用户名构造函数的完整定义,而不是cpp与main的完全定义,所以它的失败

但是:

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

告诉我l inker在哪里可以找到完整的用户名定义(在通过编译Username.cpp生成的对象文件中)。因此,通过使用Username.cpp中定义的内容来匹配未在User.cpp中定义的标识符,连接可以成功填充缺失的部分。

你可能会想 - “我已经通过包含头文件告诉编译器了,它应该知道!”。你所做的是宣称为的符号。你已经做出了承诺,最终将定义一些东西,无论是在编译这个cpp期间,还是在后来将它与通过编译另一个cpp生成的另一个目标文件链接起来。 g ++需要知道你打算从何处取出所有定义,以便它可以正确地构建最终的可执行文件。

+0

Tnx,现在我明白了;) – DkSw

3

如果您在编译过程中未添加Username.cpp,则您已经回答了您的问题。用户无法知道它。

+0

我知道如何编译它们,但我想了解为什么它以这种方式完成,因为我曾经在windows与其他工具一起工作。 ;) – DkSw

+0

这就是makefile存在的原因。 – Whiskas

0

User.cpp补充:

#include "Username.h" 
+2

'User.h'已经包含在User.h中。 –

0

这不是一个真正的C++的问题,但更多的是GCC的问题。通常情况下,你会用Make构建一个更大的程序。然后,您将有一个makefile规则,它可以构建一个目录中的所有.cpp文件并将其链接在一起。另一种方法是你的makefile明确说明哪些.cpp文件应该被编译在一起。

0

您必须将您的所有实现文件传递给g++可执行文件。第一次编译尝试失败,因为您只将User.cpp编译为目标文件并将其链接到名为main的可执行对象。

在第二次尝试中,您正确地将两个必需的实现文件传递给g++可执行文件。在这种情况下,User.cppUsername.cpp被编译成目标文件并链接在一起形成main可执行文件。在这种情况下,存在构造函数Username::Username()的实现。

2

您可以使用部分的编译,与-c标志:

g++ -c User.cpp 

这将产生一个User.o文件。

你做的每个文件

g++ -c Username.cpp 

然后您链接所有目标文件(*在这部分编译的事情。o文件):

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

通常,您会使用一些build system来自动执行此过程。因此,您还可以获得其他优势,例如不跳过自上次编译以来没有更改过的文件的重新编译。

+0

Tnx for answear – DkSw

1

如果您试图从源文件中调用除模板类的实现文件以外的模板类的成员函数,也会发生这种情况。例如:你有ABC和ABC.h和ABC.cpp(实现文件)。

只要您对通用对象的调用位于ABC.cpp中,就可以正常工作。但是,如果尝试在另一个类中包含ABC.h并指定BCD并调用对象ABC的任何成员函数,则会得到未定义的成员函数参考错误。

解决方案:为您的模板类(包括实现)创建单个头文件,以避免误导这种错误。