为什么C ++编译器找不到函数定义?

时间:2017-08-30 21:10:20

标签: c++ gcc linker

我写了一些代码试图理解编译和链接之间的关系 - 并且还要查看在源文件中必须重复的函数声明的位置。

我写的两个文件是:

Person.cpp:

#include <string>  
class Person {
  public:
   Person(std::string name) {
   (*this).name = name;
   };
   std::string getName() {
   return name;
   };
   std::string name;
};

和文件

PersonMain.cpp:

#include <iostream>
class Person {
  public:
    Person(std::string);
    std::string getName();
    std::string name;
 };
int main(){
          Person* charlie = new Person("Charlie");
          std::cout << "Hi, my name is " << charlie->getName();
          }

我在PersonMain.cpp中重复了Class Person和Class Person函数声明(而不是定义)。

我现在使用gcc C ++编译器编译和链接这两个文件:

g ++ * .cpp -o runthis.exe

然后我收到以下错误消息:

  

PersonMain.cpp :(。text + 0xfe):未定义的引用   Person::Person(std::basic_string<char, std::char_traits<char>,std::allocator<char> >)' PersonMain.cpp:(.text+0x13c): undefined reference to 人::的getName()&#39; collect2.exe:错误:ld返回1退出状态

链接时似乎找不到Person Class方法。这是为什么?我该如何治愈这个?

附录:

我明确地重复了Person in PersonMain.cpp中的Person声明,并且没有重新声明到正常情况下的头文件。所以我已经在这里完成了预处理器步骤。 This faq表明:

  

[预处理器]通过替换一次处理一个C ++源文件   包含相应文件内容的指令(通常只是声明)[...]

以及后来:

  

[链接器]通过替换引用链接所有目标文件   带有正确地址的未定义符号。每个符号   可以在其他目标文件或库中定义。

我添加了这个注释,因为@ engf-010表示编译单元的代码在该编译单元中实际上没有被编译,即使在其他编译单元中需要它也是如此。 enf-010建议我将定义和声明放入头文件中,但faq文章说只有声明应该去那里,定义可以在其他地方。

2 个答案:

答案 0 :(得分:2)

通常会将声明和定义分别分为.h.cpp个文件。

您的代码看起来像这样:

person.h - 声明:

#include <string>

class Person {
public:
        Person(std::string n);
        std::string getName() const;

        std::string name;
};

person.cpp - 实施:

#include <string>
#include "person.h"

Person::Person(const char* n) : name(n) { }
std::string Person::getName () const { return name; }

personmain.cpp - 您使用班级Person的地方:

#include <iostream>
#include "person.h"

int main()
{
        Person* charlie = new Person("Charlie");
        std::cout << "Hi, my name is " << charlie->getName ();
}

就像@engf提到的那样,基本上,你在代码中得到的是Person的双重定义而没有实现。你不能在主文件中轻松定义Person并在其他地方(你不应该)在另一个翻译单元(即Person::Person())中实现person.cpp,而不让编译器知道你实施的是什么。您无法转发声明Person并在personmain.cpp中使用它,如下所示:

class Person;
...
Person* charlie = new Person("whatever");

因为编译器应该知道完整类型以发出构造Person - 对象的代码。您不能有Person的两个定义。如果没有定义,您无法实现Person的成员,您的代码的所有用户都可以访问这些成员。所以,你剩下的就是上面这样的计划。

详细说明编译和链接,非常粗略地说,当编译器在同一个翻译单元中看到未在本地定义的符号引用时,它会将适当的记录放入目标文件中。当此目标文件传递给链接器时(可能还有一堆其他目标文件和库),链接器应将这些记录解析为输入目标文件或库中某处的符号(函数,变量等)。如果成功,它将所有引用的代码组合成单个图像。在您的情况下,链接器无法解析构造函数的引用符号Person::Person。可能this并不错Q&amp; A开始。

答案 1 :(得分:1)

您所拥有的是一个源文件中的定义和另一个源文件中的声明。 当编译包含定义的源文件时,编译器得出的结论是,没有任何真实的&#39;要生成的代码,因为您没有使用该类。 所以你最终得到一个空目标文件。

您可以在源文件中定义一个类,但如果您不在该文件中使用该类,则会将其丢弃为未使用。 如果您在其他源文件中使用该类(通过它的声明),编译器会为成员访问生成代码,但在链接期间找不到这些代码。