[英]Need help refactoring class into separate header and cpp files
[英]When to separate a small class into header and cpp files?
我知道之前已經回答了類似的問題但是我已經搜索了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個文件中的一次。 假設一個類只在程序中實例化一次,我的問題是:
我知道這里已經存在非常類似的問題,但我沒有看到任何對上述內容給出明確答案的問題。
- 將聲明和實現小類放在單個文件中是否合理?
是的,將一個小類的聲明和實現放在一個文件中是合理的。
如果沒有,為什么不呢?
因為如果修改了函數定義,則需要重新編譯依賴於類定義的所有轉換單元 - 或者更確切地說,需要重新編譯包含該定義的所有轉換單元。 這包括所有依賴於類的,因為它們必須包含定義,但也包括無償定義的定義。
- 一個類需要多大才能最好分成2個文件?
沒有硬性限制。 它取決於許多變量,並且受個人偏好的影響很大。
有些人將每個類的所有成員函數定義放在一個翻譯單元中,因為這是他們知道事情是如何完成的,或者因為他們有必須遵循的編碼標准。
其他人發誓,如果沒有通過在標題中內聯定義所有函數所允許的優化可能性,它們就無法生存,因此整個程序只有一個翻譯單元。 這還具有從頭開始減少編譯時間的效果,但也會導致整個項目在任何更改時重建。
但是沒有必要教條地遵循這些路徑中的任何一條,並且兩者都不一定是最優的 - 兩者都有缺點和優點。 因此,一個好的選擇可能介於這些路徑之間。 但正如我上面所說,選擇取決於許多變量,因此使用快速啟發式而非完整分析可能更好。 以下是一些,如果您找到合適的推理,您可以遵循:
假設一個類只在程序中實例化一次
實例化的數量與選擇無關 - 除了內聯構造函數在這種情況下對性能可能不重要。
PS
雖然它非常便攜,但#pragma once
是非標准的。 我不是說你應該擺脫它; 這只是需要注意的事情。
GetTickCount64
是特定於系統且不可移植的。 C ++在<chrono>
標頭中有一個標准時鍾API。
否則,您在.h文件中創建類的方式也沒關系,我經常在單獨的頭文件中創建枚舉。 如果一個人可以聲明一個類並提供內聯成員函數,那么,為了在同一時間定義類,為什么不這樣做呢? 它減少了文件數量,代碼量,使其更簡潔。
重要說明:我將提供一個注釋,解釋我在頭文件中聲明和定義類的原因。 其他開發人員將能夠理解我的動機。
這是另一個考慮因素: 編譯性能 。
請注意,您的一體化聲明+定義文件使用GetTickCount64
。 這個文件需要包含Windows.h - 一個相當龐大的野獸(盡管間接在你的情況下,通過你的presets.h,毫無疑問),因為GetTickCount64在Windows.h中聲明。 (如果您的計時器返回類型(u64)不依賴於特定於Windows平台的類型,則這尤其重要。
如果將聲明與定義分開,那么只有定義(.cpp文件)需要包含Windows.h。 聲明(.h)不需要它。
這意味着包含您的Timer.h文件的任何.cpp文件都不會被強制包含Windows.h。 這樣可以減少Timer.h的所有使用者的編譯時間和內存需求,最終可以加快編譯速度。 可能有許多.h文件的消費者(項目中需要包含你的Timer.h文件的許多其他.cpp文件)但是只需要編譯一個.cpp來生成Timer類的定義(你的Timer .cpp),這是唯一一個實際需要包含該函數的Windows.h頭文件的人。
隨着應用程序規模的擴大,您希望創建越來越多獨立於平台的模塊,這些模塊依賴於簡單的抽象,例如Timer類,而不是原始的Win32 / Win64 API調用,如GetTickCount64。 大部分應用程序可以只是應用程序的業務邏輯,而不依賴於包含針對大型API(如Win32 API)的重量級平台特定標頭。
第2章
這項工作的下一部分是將類的私有部分與接口的公共部分分開,以進一步隔離代碼並使頭部保持較小。 在您的特定示例中,您的類聲明中沒有任何特殊內容可以隔離,但在許多情況下,該類可能需要一個私有成員變量,其類型與公共接口不關心。 沒有必要讓你的Timer.h頭文件的使用者負擔到你可能需要放入私有變量的所有類型事物的定義; 僅限屬於您的公共API的類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.