C++ 对成员函数的未定义引用

C++ 对成员函数的未定义引用,c++,g++,C++,G++,我试图解决一个关于外部定义构造函数的编译问题 以下是有此问题的两个类: //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 s

我试图解决一个关于外部定义构造函数的编译问题

以下是有此问题的两个类:

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

如果使用“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
/tmp/ccEmWmfl.o:在函数“User::User(std::basic_string)”中:
User.cpp:(.text+0x3e):对“Username::Username(std::basic_string)”的未定义引用
collect2:ld返回了1个退出状态
如果我使用“g++User.cpp Username.cpp-o main”或者如果我使用内联构造函数/析构函数,我不会得到任何错误

这些类非常简单,但我还有很多需要编译的类,它们需要不止一个类


我不熟悉用g++在Ubuntu shell中编译,所以请有人帮我理解一下吗?

你已经回答了你的问题,如果你不在编译过程中添加Username.cpp,用户就不能知道它。

User.cpp添加:

#include "Username.h" 

这不是一个C++问题,而是一个GCC问题。通常情况下,您会使用构建一个更大的程序。然后有一个makefile规则,它在一个目录中构建所有.cpp文件并将它们链接在一起。另一种方法是,makefile专门说明哪些.cpp文件应该一起编译。

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


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

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

g++ -c User.cpp
这将生成一个User.o文件

您可以对每个文件进行部分编译

g++ -c Username.cpp
然后将所有对象文件(即*.o文件)链接在一起:

g++ User.o Username.o -o main
通常,您会使用一些来自动化这个过程。这样还可以获得其他好处,比如不跳过重新编译自上次编译以来未更改的文件

g++User.cpp

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

User::User(string s):UserUsername(new Username(s)){}
该符号应在链接阶段定义。链接是通过组合您生成的所有cpp的所有生成对象文件输出并拼凑缺少的符号来完成的。在这里,您没有告诉g++在哪里可以找到用户名构造函数的完整定义,而不是带main的cpp,因此它失败了

然而,这是:

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

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


您可能会想--“我已经通过包含头文件告诉了编译器,它应该知道!”。您所做的是声明符号。。您承诺,在编译该cpp期间,或稍后将其与编译另一cpp生成的另一个对象文件链接,最终将定义某些内容。g++需要知道您打算从何处提取所有定义,以便它可以构建最终的执行表正确。

如果您试图从源文件而不是模板类的实现文件调用模板类的成员函数,也可能发生这种情况。例如:您的clas ABC带有ABC.h和ABC.cpp(实现文件)

只要您对泛型对象的调用位于ABC.cpp中,所有调用都可以正常工作。但是,如果您尝试在另一个类(如BCD)中包含ABC.h并调用对象ABC的任何成员函数,您将得到对成员函数错误的未定义引用


解决方案:为模板类创建一个包含实现的头文件,以避免此错误产生误导。

请注意,在头文件中使用
名称空间XXX
被认为是非常糟糕的做法。为什么不使用Makefile或更方便的方法呢?我知道如何编译它们,但我想了解d为什么这样做,因为我以前在windows中使用其他工具。;)这就是makefiles存在的原因。
g++ -c Username.cpp
g++ User.o Username.o -o main
User::User(string s):UserUsername(new Username(s)){}