我正在尝试解决有关外部定义的构造函数的编译问题。
以下是有这个问题的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”或者如果我使用内联构造函数/析构函数,则不会出现错误。
这些类非常简单,但我需要更多编译才能使用多个类。
我是用g ++编译Ubuntu shell的新手,所以请有人帮我理解吗?
答案 0 :(得分:3)
如果你没有在编译过程中添加Username.cpp,你已经回答了你的问题。用户无法知道它。
答案 1 :(得分:3)
g ++ User.cpp
这会编译User.cpp并尝试从中创建可执行文件(即调用链接器)。由于User.cpp在User.cpp中没有完全定义的符号(此处的用户名构造函数:
User::User(string s):UserUsername(new Username(s)){}
该符号应在链接阶段定义。链接是通过组合您生成的所有cpps的所有生成的目标文件输出并将缺失的符号拼接在一起来完成的。在这里你没有告诉g ++在哪里可以找到除了cpp with main之外的Username构造函数的完整定义,所以它失败了。
但是:
g ++ User.cpp Username.cpp -o main
告诉链接器在哪里可以找到完整的用户名定义(在通过编译Username.cpp生成的目标文件中)。因此,链接可以通过使用Username.cpp中定义的whats来匹配未在User.cpp中定义的标识符来成功填写缺失的部分。
你可能会想 - “我已经通过包含头文件告诉编译器了,它应该知道!”。您所做的是声明符号。您已经承诺最终会定义某些内容,无论是在编译该cpp期间还是稍后将其与通过编译另一个cpp生成的另一个目标文件相关联。 g ++需要知道你打算从哪里获取所有定义,以便它可以正确构建最终的可执行文件。
答案 2 :(得分:2)
您可以使用部分编译,-c
标志:
g++ -c User.cpp
这会生成一个User.o文件。
您在每个文件上执行此部分编译
g++ -c Username.cpp
然后将所有目标文件(* .o文件)链接在一起:
g++ User.o Username.o -o main
通常,您会使用一些build system来自动完成此过程。有了这个,你还可以获得其他优势,比如不跳过自上次编译以来没有改变的文件的重新编译。
答案 3 :(得分:1)
如果您尝试从除模板类的实现文件之外的源文件调用模板类的成员函数,也会发生这种情况。例如:你有ABC与ABC.h和ABC.cpp(实现文件)。
只要您对泛型对象的调用位于ABC.cpp中,一切运行良好。但是,如果您尝试在另一个类中包含ABC.h并说BCD并调用对象ABC的任何成员函数,您将获得未定义的引用成员函数错误。
解决方案:为模板类创建单个头文件,包括实现以避免此错误,这会产生误导。
答案 4 :(得分:0)
User.cpp add:
#include "Username.h"
答案 5 :(得分:0)
这不是一个真正的C ++问题,而是更多的GCC问题。通常,您使用Make
构建更大的程序。然后,您有一个makefile规则,可以在目录中构建所有.cpp文件并将链接连接在一起。另一种方法是makefile特别说明应该一起编译哪些.cpp文件。
答案 6 :(得分:0)
您必须将所有实施文件传递给g++
可执行文件。第一次编译尝试失败,因为您只是将User.cpp
编译为目标文件并将其链接到名为main
的可执行对象。
在第二次尝试中,您正确地将两个必要的实现文件传递给g++
可执行文件。在这种情况下,User.cpp
和Username.cpp
被编译为目标文件并链接在一起以形成main
可执行文件。在这种情况下,存在Username::Username()
构造函数的实现。