我知道之前已经回答过类似的问题,但是我已经搜索了stackoverflow(等等),但是对于在该程序中实例化并仅使用一次的小类,该如何处理却没有一个明确的想法。仍然将声明和实现放在单独的文件中真的很重要吗?
采用以下示例:
// timer.hpp
#pragma once
#include "presets.h" // includes #defines for u64 -> uint64_t, etc
class Timer {
public:
Timer() {}
Timer(u64 elt) : elt_(elt) {}
void startTiming() { if (NOT running_){ running_ = true; sTime_ = GetTickCount64(); }};
void stopTiming() { if (running_) { running_ = false; eTime_ = GetTickCount64(); elt_ += (eTime_ - sTime_); sTime_ = eTime_; }}
u64 getElapsed() { if (NOT running_) return elt_; eTime_ = GetTickCount64(); return elt_ + eTime_ - sTime_; }
private:
bool running_ = true;
u64 elt_ = 0, eTime_ = 0, sTime_ = GetTickCount64();
};
我读过的所有东西都坚持声明和实现在单独的文件中,但是将这样一个简单的类分为.h文件和.cpp文件似乎是荒谬的。它很少更改,只能实例化一次。
我还有一些其他的类,稍大一些,并且仅在2个文件中使用一次。假设一个类仅在程序中实例化一次,我的问题是:
我知道这里已经存在非常类似的问题,但是我还没有读过任何对上述内容给出明确答案的问题。
答案 0 :(得分:5)
- 将小类的声明和实现放在单个文件中是否合理?
是的,将小类的声明和实现放在单个文件中是合理的。
如果没有,为什么不呢?
因为如果函数定义被修改,则依赖于类定义的所有翻译单元都需要重新编译-或更确切地说,包括该定义的所有翻译单元都需要重新编译。这包括所有确实依赖于类的对象,因为它们必须包含定义,但也包括那些无缘无故地包含定义的对象。
- 将一个类分成两个文件之前,一个类需要多大?
没有硬性限制。它取决于许多变量,并且受个人偏好的影响很大。
有人将每个类的所有成员函数定义都放在一个翻译单元中,因为这是他们了解事情的方式,或者因为他们必须遵循编码标准。
其他人发誓,如果没有通过在标头中内联定义所有函数来允许优化的可能性,就无法生存,因此整个程序只有一个翻译单元。这样还可以减少从头开始的编译时间,还可以使整个项目在进行任何更改时都可以重建。
但是没有必要教条地遵循这两个路径,而且都不是最优的-两者都有缺点和优点。因此,在这些路径之间的某个地方可能是个不错的选择。但是正如我在上面说的那样,选择取决于许多变量,因此使用快速试探法而不是进行全面分析可能会更好。如果您找到合适的推理方法,可以参考以下几点:
假设一个类仅在程序中实例化一次
实例化的数量与选择基本上无关-在这种情况下,除了内联构造函数对于性能而言可能并不重要。
P.S。
尽管它非常便携,但是#pragma once
是非标准的。我并不是说您应该摆脱它;这只是要注意的事情。
GetTickCount64
是特定于系统的,不可移植。 C ++在<chrono>
标头中有一个标准时钟API。
答案 1 :(得分:2)
否则,您也可以在.h文件中创建类的方式,我经常在单独的头文件中创建枚举。如果可以声明一个类并提供内联成员函数,以便同时定义该类,为什么不这样做呢?它减少了文件数量,减少了代码量,使其更简洁。
重要说明::我将提供一条注释,解释为什么我在头文件中声明和定义了一个类。其他开发人员将能够理解我的动机。
答案 2 :(得分:1)
这是另一个考虑因素:编译性能。
请注意,您的多合一声明和定义文件使用了GetTickCount64
。该文件需要包含Windows.h-一个相当大的野兽(尽管在您的情况下,这是间接地,通过presets.h毫无疑问),因为GetTickCount64是在Windows.h中声明的。 (如果您的计时器返回类型(u64)不依赖于Windows平台特定的类型,则这尤其重要。)
如果将声明与定义分开,则仅定义(.cpp文件)将需要包含Windows.h。并且声明(.h)不需要它。
这意味着任何包含Timer.h文件的.cpp文件都不会被强制包括Windows.h。这使所有Timer.h使用者的编译时间和内存需求降低了,并最终导致了更快的编译速度。 .h文件的使用者很多(项目中的许多其他.cpp文件都需要包含Timer.h文件),但是只需编译一个.cpp即可生成Timer类的定义(您的Timer .cpp),而这实际上是唯一需要包含该功能的Windows.h标头的文件。
随着应用程序大小的增长,您将希望创建越来越多的平台独立模块,这些模块依赖于简单的抽象(例如Timer类),而不是原始的Win32 / Win64 API调用(例如GetTickCount64)。您的应用程序的大部分只是您应用程序的业务逻辑,而不必依赖于包括大型API(例如Win32 API)的特定平台的标头。
第二章
此工作的下一部分将是将类的私有部分与接口的公共部分分开,以进一步隔离代码并使标头保持较小。在您的特定示例中,您的类声明中没有什么要隔离的特殊东西,但是在许多情况下,该类可能需要一个私有成员变量,而该私有变量的类型不是公共接口所关心的。无需给Timer.h头文件的使用者增加定义您可能需要在私有变量中放置的所有类型的内容的负担;仅属于您的公共API的类型。