良好的 C++ 類設計不僅關乎代碼能否編譯通過,更直接影響程序的可維護性、擴展性與運行效率。本文以一個 complex 複數類的實現為線索,系統梳理類設計中的八大核心習慣,並進階討論帶指針成員類的「三大件」實現、static 成員的正確用法及單例模式實踐。
防止頭文件被重複包含導致重複定義錯誤,是 C++ 頭文件的標準守則。
// complex.h
#ifndef __COMPLEX__
#define __COMPLEX__
class complex { /* ... */ };
#endif
也可使用 #pragma once(編譯器擴展),但傳統宏防衛移植性更強。
將數據成員置於 private,對外提供 const 存取器,實現封裝與資訊隱藏。
class complex {
public:
double real() const { return re; }
double imag() const { return im; }
private:
double re, im;
};
const 正確性:承諾不修改對象狀態所有不修改數據成員的成員函數均應加上 const 資格符。這不僅讓編譯器幫你檢查,也向調用者明確表達意圖。關鍵點:const 對象只能調用 const 成員函數。
double real() const { return re; } // 正確
// double real() { return re; } // 錯誤:const 對象無法調用
初始值列表執行的是「初始化」,函數體內賦值則是「賦值」。對於 const、引用成員或無默認構造函數的類成員,初始值列表是唯一選擇;對內置類型亦能避免默認構造後再賦值的開銷。
complex(double r = 0, double i = 0) : re(r), im(i) { }
const T&為類類型參數使用 const 引用,避免不必要的拷貝構造與析構開銷。內置類型(char、int 等)值傳遞效率通常更高,但為風格統一,亦可一律採用引用。
complex& operator+=(const complex& rhs); // 避免拷貝,const 保護參數不被修改
返回引用可避免拷貝,支持鏈式調用(如 c3 += c2 += c1)。但絕不能返回局部變量的引用(生命週期結束導致未定義行為)。判斷依據:被返回對象在函數調用前已存在於內存中。
inline complex operator+(const complex& x, const complex& y);
inline complex operator+(const complex& x, double y);
inline complex operator+(double x, const complex& y);
類的使用者關心的是「能做什麼」,而非「怎麼實現」。將公有接口置頂,閱讀者一眼即見核心功能,實現細節(private 數據、輔助函數)後置。
當類包含指針成員(如動態分配內存的字符串類 String)時,編譯器默認的淺拷貝會導致雙重釋放、內存洩漏、野指針等嚴重問題。必須顯式實現:
String::~String() { delete[] m_data; }
String& String::operator=(const String& str) {
if (this == &str) return *this; // ① 自賦值檢查
delete[] m_data; // ② 釋放自身舊內存
m_data = new char[strlen(str.m_data) + 1]; // ③ 分配新內存
strcpy(m_data, str.m_data); // ④ 深拷貝內容
return *this;
}
拷貝構造函數同理,只做深拷貝(無需釋放舊資源)。
若 a = a,未檢查則 delete[] m_data 會釋放掉右值也在引用的內存,隨後 strcpy 讀取已釋放內存 → 未定義行為。
static 與類:靜態成員的正確理解與應用銀行利率、對象計數器等「全局共享、單份存儲」的數據應聲明為 static。它們存儲於全局/靜態存儲區,程序啟動時構造,結束時析構。
this 指針對象.函數() 或 類名::函數() 調用
class A { static int a; }; // 類內聲明
int A::a = 10; // 類外定義(分配內存)