如果可能,我想删除< vector>的包含内容和< string>从我的类头文件。 string和vector都是头文件中声明的函数的返回类型。
我希望我可以做类似的事情:
namespace std {
template <class T>
class vector;
}
并且,在标题中声明向量并将其包含在源文件中。
是否有参考文献涵盖您必须包含在标题中的情况,以及您可以将包含纳入源文件的情况?
答案 0 :(得分:14)
您无法安全地转发声明STL模板,至少如果您想要便携且安全地执行它。该标准清楚地表明了每个STL元素的最低要求,但为实现扩展留出了空间,可以添加额外的模板参数,只要它们具有默认值。也就是说:标准规定std :: vector是一个模板,它至少需要2个参数(类型和分配器),但在符合标准的实现中可以有任意数量的额外参数。
不包括字符串和向量标题有什么意义?当然,无论谁打算使用你的课程,都必须已经包含它,因为它在你的界面上。
当您询问有关何时包含以及何时转发声明的参考时,我的建议是:包括您界面中的所有内容,转发声明内部详细信息。
这里有更多的问题,简单的编译性能。如果您推送在标头之外的公共(或受保护)接口中的类型的包含,您将创建包含顺序的依赖项。用户必须知道在包含标题之前必须包含 string ,因此您需要为他们提供一件令人担心的事情。
实现文件中应包含哪些内容:实现细节,记录器,不影响接口的元素(数据库连接器,文件头),内部实现细节(即使用STL算法实现不会影响您的界面,为简单目的而创建的仿函数,实用程序......)
答案 1 :(得分:11)
除少数例外情况外,您不允许向std添加内容:命名空间。对于像vector和string这样的类,你没有选择,只能#include相关的标准头文件。
另外,请注意字符串不是类,而是basic_string<char>
的typedef。
答案 2 :(得分:6)
这对矢量或字符串没有帮助,但值得一提的是iostream有一个前向引用标题,称为iosfwd。
答案 3 :(得分:4)
标准容器通常具有其他默认模板参数(分配器等),因此这不起作用。例如,这是GNU实现的一个片段:
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector : protected _Vector_base<_Tp, _Alloc>
{ ... };
答案 4 :(得分:4)
这也是我之前尝试做的事情,但由于模板原因,这是不可能的。
请参阅我的问题:Forward Declaration of a Base Class
由于这样的标题在开发过程中不会改变,所以无论如何都不值得优化......
答案 5 :(得分:4)
没有简单明显的方法(正如其他人已经很好地解释过的那样)。
但是这些标题应该被视为语言的一部分(真的!),所以你可以把它们放在你自己的标题中,没有任何问题,没有人会抱怨。
如果您的关注点是编译速度,我建议您使用预编译的头文件并将这些std头文件放入其中(除此之外)。它将显着提高您的编译速度。
对不起,对于“真正的赢家是避免战斗的人”的回答。
答案 6 :(得分:3)
如果字符串和向量仅用于您班级的非公开成员的签名,则可以使用PImpl习语:
// MyClass.h
class MyClassImpl;
class MyClass{
public:
MyClass();
void MyMethod();
private:
MyClassImpl* m_impl;
};
// MyClassImpl.h
#include <vector>
#include <string>
#include <MyClass.h>
class MyClassImpl{
public:
MyClassImpl();
void MyMethod();
protected:
std::vector<std::string> StdMethod();
};
// MyClass.cpp
#include <MyClass.h>
#include <MyClassImpl.h>
void MyClass::MyMethod(){
m_impl->MyMethod();
}
您始终在头文件中包含vector和string,但仅限于类的实现部分;仅包含MyClass.h的文件不会拉入字符串和向量。
答案 7 :(得分:3)
只需在您引用STL集合的任何文件中包含标题。
正如其他人所提到的,没有办法可靠地转发声明STL类,即使你找到了一个特定的实现,如果你使用不同的STL实现,它可能会中断。
如果编译单元没有实例化类,它将不会使您的目标文件更大。
答案 8 :(得分:2)
该语言允许您派生自己的类:
// MyKludges.h
#include <vector>
#include <string>
class KludgeIntVector : public std::vector<int> {
// ...
};
class KludgeDoubleVector : public std::vector<double> {
// ...
};
class KludgeString : public std::string {
// ...
};
更改您的功能以返回KludgeString和KludgeIntVector。由于这些不再是模板,您可以在头文件中转发声明它们,并在实现文件中包含MyKludges.h。
严格地说,派生类不会继承基类构造函数,析构函数,赋值运算符和朋友。您将需要提供(正常)任何您正在使用的实现。
// LotsOfFunctions.h
// Look, no includes! All forward declared!
class KludgeString;
// 10,000 functions that use neither strings nor vectors
// ...
void someFunction(KludgeString &);
// ...
// Another 10,000 functions that use neither strings nor vectors
// someFunction.cpp
// Implement someFunction in its own compilation unit
// <string> and <vector> arrive on the next line
#include "MyKludges.h"
#include "LotsOfFunctions.h"
void someFunction(KludgeString &k) { k.clear(); }
答案 9 :(得分:1)
也许你最好使用pimpl习语:在我看来,你不希望将你的类的实现暴露给客户端代码。如果向量和字符串对象按值聚合,则编译器需要查看其完整声明。
答案 10 :(得分:1)
除了向std::swap
添加重载(我现在能想到的唯一例外)之外,通常不允许向std
命名空间添加任何内容。即使允许,std::vector
的实际声明也比OP中的代码复杂得多。有关示例,请参阅Nikolai N Fetissov's answer。
除此之外,您还有一个问题,即您的班级用户将使用返回std::vector
或std::string
的函数执行哪些操作。 C++ Standard section 3.10表示返回此类对象的函数返回 rvalues ,而rvalues必须是完整类型。在英语中,如果您的用户想要对这些功能执行任何操作,则必须#include <vector>
或<string
&gt;无论如何。我认为#include
文件中的.h
标题会更容易,并且可以使用它完成。
答案 11 :(得分:1)
我认为你的目标是加快编译时间?否则我不确定你为什么要删除它们。
另一种方法(不漂亮但实用)是在包含本身周围使用宏观保护。
e.g。
#ifndef STDLIB_STRING
#include <string>
#define STDLIB_STRING
#endif
虽然这看起来很混乱,但在大型代码库上确实会增加编译时间。我们所做的是创建一个自动生成防护的Visual Studio宏。我们将宏绑定到一个键以便于编码。然后它就成了公司编码标准(或习惯)。
我们也为我们自己的包括。
e.g。
#ifndef UTILITY_DATE_TIME_H
#include "Utility/DateTime.h"
#endif
由于我们在创建自己的头文件时有Visual Studio助手来自动生成防护,因此我们不需要#define。宏知道它是内部包含因为我们总是使用
#include ""
格式
#include <>
for external includes。
我知道它看起来并不漂亮,但它确实加快了我们在较大的代码库上编译时间超过半小时(从内存中)。