对象类成员作为避免标题中#include的指针 - 这是一个好习惯吗?

时间:2014-01-14 23:44:08

标签: class pointers include header-files forward-declaration

这实际上是一个优先级问题:在C ++中哪个更受欢迎,避免指针或避免头文件中的#includes?

“不要在头文件中使用#include。”

根据我的研究,似乎有些含糊不清。在这个问题中,最重要的答案是“......确保你真的需要一个包含,[不要使用一个],当一个前瞻性声明,甚至完全放弃它时。” (来自Header files and include best practice

本文解释了多余的头部包含可能对编译时产生的负面影响:http://blog.knatten.org/2012/11/09/another-reason-to-avoid-includes-in-headers/

除了本教程,还要说明,“......您应该尝试将所有代码放在CPP类中,而只放入HPP文件中的类声明。”:https://github.com/LaurentGomila/SFML/wiki/Tutorial%3A-Basic-Game-Engine#wiki-declarations

“不要使用指针。”

但是,也有证据表明最常应避免使用指针:

哪种偏好优先?

如果我对在头文件中避免#includes的理解是正确的,可以通过将类成员更改为指针来轻松完成,这样我就可以使用前向声明,但这对于只有生命周期的类成员来说是个好主意和班级本身一样长吗?

1 个答案:

答案 0 :(得分:3)

这不是一个“一个或另一个”。这两种说法都是正确的,但你需要理解它们背后的推理。

tl; dr:尽可能使用前向声明来减少编译时间。尽可能使用堆栈对象或引用,仅在极少数情况下使用指针。

“请勿在标头文件中使用#include。”

这是一个相当笼统的陈述,因为它是错误的。这个陈述背后更重要的部分实际上是:“使用前向声明尽可能”。包含头文件本身并不是坏事,但它们通常也不需要。

如果包含的类型/类/等,可以使用前向声明。用作新类型/类/ etc中的指针。给定标题内的声明。前向声明只是告诉编译器:“在某个地方你可以找到类型X的实际声明。”如果声明中根本没有使用类型,甚至可以删除包含。原因是编译器不需要知道有关这些类型的任何信息来计算新类型所需的内存布局。例如,指针“始终”具有相同的大小。在头文件中另外包含文件可能只会浪费处理能力,因为编译器必须打开并解析文件,从而为编译时添加了昂贵的秒数。因此,在大多数情况下,您可以通过减少头文件中不必要的包含而使用前向声明来帮助自己。

为了完成:如果获得循环引用,则明确需要前向声明(类A依赖于类B,它取决于类C,它取决于类A)。然而,这通常也会揭示糟糕的设计和/或旧的/过时的编码标准,这些标准将引导我们进入第二个主题。

“不要使用指针。”

此声明再次过于笼统。人们可能更愿意说:“不要使用原始指针。”

使用C ++ 11和很快C ++ 1y语言本身已经发生了很大变化。正如世界上看到的那些糟糕的C ++书籍一样,现在更多过时的C ++书籍浮出水面(但这里只有good list)。虽然过去我们主要使用指针newdelete进行内存管理,但我们已经发展到更好,更易读,风险更低,并且100%内存无泄漏的方式来管理内存中的数据。其中一个神奇的词是RAII - 因为你从上面的SFML链接了一些东西,这里是RAII力量的a nice demonstration。我看到许多人使用指针newdelete只是因为或者因为他们在思考Java或C#术语,所以使用new关键字对对象进行实例化。但是在C ++中,对象不需要使用new进行分配,最好在堆栈而不是堆上运行。这适用于许多很多事情,特别是在使用STL容器时,它会隐藏后台的动态管理。如果您需要数据是动态的,非“本地”或者您需要大量的数据,那么堆的使用大多数情况下都是优选的。但是,当您使用堆时,请确保使用智能指针,例如std::unique_ptrstd::shared_ptr,具体取决于用例,但肯定不是原始指针。在现代C ++中,原始指针永远不应该拥有一个对象。有些情况下可以返回原始指针来引用一个对象,但现代C ++中没有理由在原始指针上调用new

让我们回到最初的问题。 “不要使用原始指针”本质上更多的是一个设计问题,与整个标题问题完全无关。虽然在某些情况下你可能需要切换到原始指针,但由于循环引用,前向声明的使用只是编译时间(也许是干净的代码),但它对编程本身并不重要。

简而言之:不要使用原始指针来避免头文件中包含内容,而是尽可能使用前向声明并尽可能地使用智能指针。