【问题标题】:Undefined Reference To Member function未定义对成员函数的引用
【发布时间】:2011-11-04 14:50:36
【问题描述】:

我正在尝试解决有关外部定义构造函数的编译问题。

这里有 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 中编译的新手,所以请有人帮我理解一下吗?

【问题讨论】:

  • 请注意,在头文件中包含using namespace XXX 被认为是非常糟糕的做法。
  • 为什么不使用 Makefile 或者更方便的build system

标签: c++ g++


【解决方案1】:

g++ 用户.cpp

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

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

符号应在链接阶段定义。链接是通过组合您生成的所有 cpp 的所有生成的目标文件输出并将丢失的符号拼凑在一起来完成的。在这里,您没有告诉 g++ 在哪里可以找到 Username 构造函数的完整定义,而不是带有 main 的 cpp,因此它失败了。

但是:

g++ User.cpp 用户名.cpp -o main

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我知道如何编译它们,但我想了解为什么它会以这种方式完成,因为我曾经使用其他工具在 windows 中工作。 ;)
    • 这就是makefile存在的原因。
    【解决方案3】:

    您可以使用带有-c 标志的部分编译:

    g++ -c User.cpp
    

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

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

    g++ -c Username.cpp
    

    然后将所有目标文件(*.o 文件)链接在一起:

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

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

    【讨论】:

      【解决方案4】:

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

      只要您对通用对象的调用位于 ABC.cpp 中,一切正常。但是,如果您尝试将 ABC.h 包含在另一个类(例如 BCD)中并调用对象 ABC 的任何成员函数,您将收到 Undefined Reference To Member function 错误。

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

      【讨论】:

        【解决方案5】:

        User.cpp 添加:

        #include "Username.h" 
        

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-05-27
              • 2012-03-10
              • 1970-01-01
              • 2013-11-06
              • 2012-12-20
              • 2019-04-29
              • 2021-12-13
              相关资源
              最近更新 更多