我在尝试编译在 .hpp
和 .cpp
文件之间拆分的 C++ 模板类时遇到错误:
$ g++ -c -o main.o main.cpp
$ g++ -c -o stack.o stack.cpp
$ g++ -o main main.o stack.o
main.o: In function `main':
main.cpp:(.text+0xe): undefined reference to 'stack<int>::stack()'
main.cpp:(.text+0x1c): undefined reference to 'stack<int>::~stack()'
collect2: ld returned 1 exit status
make: *** [program] Error 1
这是我的代码:
stack.hpp:
#ifndef _STACK_HPP
#define _STACK_HPP
template <typename Type>
class stack {
public:
stack();
~stack();
};
#endif
stack.cpp:
#include <iostream>
#include "stack.hpp"
template <typename Type> stack<Type>::stack() {
std::cerr << "Hello, stack " << this << "!" << std::endl;
}
template <typename Type> stack<Type>::~stack() {
std::cerr << "Goodbye, stack " << this << "." << std::endl;
}
main.cpp:
#include "stack.hpp"
int main() {
stack<int> s;
return 0;
}
ld
当然是正确的:符号不在 stack.o
中。
this question 的答案|没有帮助,因为我已经按照它说的那样做。
This one可能会有所帮助,但我不想将每个方法都移到 .hpp
文件中——我不应该这样做,不是吗?
是将 .cpp
文件中的所有内容移动到 .hpp
文件中的唯一合理解决方案,并简单地包含所有内容,而不是作为独立的目标文件链接?这看起来非常丑陋!在这种情况下,我还不如恢复到以前的状态并将 stack.cpp
重命名为 stack.hpp
并完成它。
最佳答案
无法将模板类的实现写在单独的 cpp 文件中并编译。如果有人声称,这样做的所有方法都是模仿单独 cpp 文件使用的解决方法,但实际上,如果您打算编写模板类库并将其与头文件和 lib 文件一起分发以隐藏实现,那根本不可能.
要知道为什么,让我们看一下编译过程。头文件永远不会被编译。它们只是经过预处理。然后将预处理的代码与实际编译的 cpp 文件合并。现在,如果编译器必须为对象生成适当的内存布局,它需要知道模板类的数据类型。
其实必须明白,模板类根本不是一个类,而是一个类的模板,它的声明和定义是编译器在编译时从参数中得到数据类型的信息后生成的。只要无法创建内存布局,就无法生成方法定义的指令。请记住,类方法的第一个参数是“this”运算符。所有类方法都转换为具有名称修饰和第一个参数作为其操作对象的单独方法。 'this' 参数实际上告诉了编译器无法使用模板类的对象的大小,除非用户使用有效的类型参数实例化对象。在这种情况下,如果您将方法定义放在单独的 cpp 文件中并尝试编译它,则不会使用类信息生成目标文件本身。编译不会失败,它会生成目标文件,但不会为目标文件中的模板类生成任何代码。这就是链接器无法在目标文件中找到符号并且构建失败的原因。
现在隐藏重要实现细节的替代方法是什么?众所周知,将接口(interface)与实现分离的主要目标是以二进制形式隐藏实现细节。这是您必须将数据结构和算法分开的地方。您的模板类必须只表示数据结构而不是算法。这使您能够在单独的非模板化类库中隐藏更有值(value)的实现细节,其中的类将在模板类上工作或仅使用它们来保存数据。模板类实际上将包含更少的代码来分配、获取和设置数据。其余的工作将由算法类完成。
我希望这个讨论会有所帮助。
关于c++ - 将模板化的 C++ 类拆分为 .hpp/.cpp 文件——这可能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1724036/