C++;类声明和名称空间 我正试图编写一个C++库。这是通常的设置,一个cpp文件,一个头文件。我希望头文件只公开要使用的部分(例如,我有一个抽象基类,我不希望在头文件中)。到目前为止,我只处理一个文件(我认为这应该没有什么区别,因为包含是由预处理器完成的,它不关心任何事情)
您将注意到,“头文件”分布在头实现文件之前和之后的两个点上C++;类声明和名称空间 我正试图编写一个C++库。这是通常的设置,一个cpp文件,一个头文件。我希望头文件只公开要使用的部分(例如,我有一个抽象基类,我不希望在头文件中)。到目前为止,我只处理一个文件(我认为这应该没有什么区别,因为包含是由预处理器完成的,它不关心任何事情),c++,templates,class,namespaces,header-files,C++,Templates,Class,Namespaces,Header Files,您将注意到,“头文件”分布在头实现文件之前和之后的两个点上 #include <stdio.h> // lib.h namespace foo { template <class T> class A; } // lib.cpp namespace foo { template <class T> class A { private: T i; public: A(T i) {
#include <stdio.h>
// lib.h
namespace foo {
template <class T> class A;
}
// lib.cpp
namespace foo {
template <class T> class A {
private:
T i;
public:
A(T i) {
this->i = i;
}
T returnT() {
return i;
}
};
};
// lib.h
namespace foo {
template <class T> T A<T>::returnT();
}
// foo.cpp
void main() {
foo::A<int> a = foo::A<int>(42);
printf("a = %d",a.returnT());
}
这是公认的做法吗?如果可能的话,我想要一个更抽象的头文件。不能将模板的定义与其声明分开。它们必须一起进入头文件 对于“为什么?”我建议阅读
我可能误解了你的问题。为了解决您的问题,以下内容无效:
namespace foo {
template <class T> class A;
template <class T> T A<T>::returnT();
}
namespace foo {
class A;
int A::returnT();
}
成员函数必须在类的定义中声明。此处处理的.cpp文件存在两个问题: 一,。 如果要将该类的实例放在堆栈上(就像在main()中那样),编译器需要知道该类的大小(以分配足够的内存)。为此,它需要了解成员,并以此了解完整的声明 隐藏类布局的唯一方法是建立一个接口和一个工厂方法/函数,并将实例放在工厂的堆上 例如(没有模板;请参见下文了解原因): 然后在.cpp中执行以下操作:
namespace foo {
class A : public IA {
private:
int i;
public:
A() :
i(0) {
}
virtual ~A() {
}
virtual int returnT() {
return i;
}
};
IA::~IA() {
}
IA *IA::Create() {
return new A();
}
}
顺便说一句:建议使用智能指针
二,。
由于您使用的是模板,因此方法定义必须通过头文件可见,或者为特定类型集显式实例化
因此,您可以将代码拆分为lib.h和lib_impl.h:
namespace foo {
template <typename T> class A : public IA<T> {
private:
T i;
public:
A() :
i(T()) {
}
virtual ~A() {
}
virtual T returnT() {
return i;
}
};
template <typename T> IA<T> *IA<T>::Create() {
return new A<T>();
}
}
lib.h:
namespace foo {
template <typename T> class IA {
public:
virtual ~IA() {
}
virtual T returnT() = 0;
static IA *Create();
};
}
名称空间foo{
模板类IA{
公众:
虚拟~IA(){
}
虚拟T returnT()=0;
静态IA*Create();
};
}
lib_impl.h:
namespace foo {
template <typename T> class A : public IA<T> {
private:
T i;
public:
A() :
i(T()) {
}
virtual ~A() {
}
virtual T returnT() {
return i;
}
};
template <typename T> IA<T> *IA<T>::Create() {
return new A<T>();
}
}
名称空间foo{
模板类别A:公共IA{
私人:
TⅠ;
公众:
A():
i(T()){
}
虚拟~A(){
}
虚拟T returnT(){
返回i;
}
};
模板IA*IA::创建(){
返回新的A();
}
}
因此,在需要请求的地方包括lib_impl.h。
要使用显式实例化,请添加lib.cpp并允许该文件包含lib_impl.h:
namespace foo {
template <typename T> class A : public IA<T> {
private:
T i;
public:
A() :
i(T()) {
}
virtual ~A() {
}
virtual T returnT() {
return i;
}
};
template <typename T> IA<T> *IA<T>::Create() {
return new A<T>();
}
}
lib.cpp:
#include <lib_impl.h>
namespace foo {
template class IA<int>;
template class A<int>;
template class IA<float>;
template class A<float>;
template class IA<char>;
template class A<char>;
// ...
}
#包括
名称空间foo{
模板类IA;
甲级模板;
模板类IA;
甲级模板;
模板类IA;
甲级模板;
// ...
}
整个第一块代码是头文件代码吗?我很困惑,因为有两个标记为“lib.h”的块,但是在那里有一个main
函数……在编写单词template
时习惯于披露实现细节。这就是它的工作原理,句号。您可以制作“实现头文件”,将其#包括在主头文件中,但必须提供源文件。@James McNellis:第一个代码段(灰色块)只是一个测试文件,我用来将所有内容保存在一个文件中。其想法是将其分为三个文件,lib.h
、lib.cpp
和main.cpp
,最后一个只是“用户”测试,调用lib
。问题在于,为了得到我想要的东西,我需要将头部拆分为实现代码上方的一部分和下方的一部分。这有意义吗?为了补充这个伟大的答案,您还可以在lib.h的末尾包含lib#u impl.h。这样,从用户的角度来看,快速查看lib.h将揭示基本内容(界面),而不需要查看lib_impl.h中的详细信息,也不需要做任何特殊工作来确定何时应该包含lib_impl.h。这种方法(也在常见问题解答中概述)就是我最终采用的方法。然而,智能指针并不是我所处的位置;)我有个问题。据我所知,lib.cpp被用作编译器的一种“暗示”,告诉编译器要生成哪些模板。常见问题解答也提到了这种方法(第35.13项),但是这种语法具体做了什么?例如,我可以在Stroustrups的书中找到一些东西?@Svend:显式实例化在lib.cpp的编译单元中为特定的实例化模板生成所有方法。因此,模板类的使用者可以使用,即a,而不包括lib_impl.h,因为链接器可以从lib.dll/lib.So解析方法。优点是:隐藏实现,如果经常使用,编译速度更快。缺点:您不能使用…@Svend:来稍微澄清一下“更快的编译时间”:如果实现在lib.h中,或者如果lib_impl.h包含在所有地方,那么编译器将在其使用的每个编译单元中实例化所使用的方法(可能是相同方法的一百倍)。由链接器稍后删除所有冗余实例化…这需要时间。。。。
#include <lib_impl.h>
namespace foo {
template class IA<int>;
template class A<int>;
template class IA<float>;
template class A<float>;
template class IA<char>;
template class A<char>;
// ...
}