我写了一些代码试图理解编译和链接之间的关系 - 并且还要查看在源文件中必须重复的函数声明的位置。
我写的两个文件是:
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文章说只有声明应该去那里,定义可以在其他地方。
答案 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;要生成的代码,因为您没有使用该类。 所以你最终得到一个空目标文件。
您可以在源文件中定义一个类,但如果您不在该文件中使用该类,则会将其丢弃为未使用。 如果您在其他源文件中使用该类(通过它的声明),编译器会为成员访问生成代码,但在链接期间找不到这些代码。