首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 類設計精要:從複數類看良好編程習慣

     編輯:關於C++

前言:以複數類為例探討 C++ 類設計原則

良好的 C++ 類設計不僅關乎代碼能否編譯通過,更直接影響程序的可維護性、擴展性與運行效率。本文以一個 complex 複數類的實現為線索,系統梳理類設計中的八大核心習慣,並進階討論帶指針成員類的「三大件」實現、static 成員的正確用法及單例模式實踐。

一、 類定義的八大良好習慣

1. 頭文件防衛式聲明

防止頭文件被重複包含導致重複定義錯誤,是 C++ 頭文件的標準守則。

// complex.h
#ifndef __COMPLEX__
#define __COMPLEX__

class complex { /* ... */ };

#endif

也可使用 #pragma once(編譯器擴展),但傳統宏防衛移植性更強。

2. 數據封裝:private 數據 + public 接口

將數據成員置於 private,對外提供 const 存取器,實現封裝與資訊隱藏。


class complex {
public:
    double real() const { return re; }
    double imag() const { return im; }
private:
    double re, im;
};

3. const 正確性:承諾不修改對象狀態

所有不修改數據成員的成員函數均應加上 const 資格符。這不僅讓編譯器幫你檢查,也向調用者明確表達意圖。關鍵點:const 對象只能調用 const 成員函數。


double real() const { return re; } // 正確
// double real() { return re; }    // 錯誤:const 對象無法調用

4. 善用構造函數初始值列表

初始值列表執行的是「初始化」,函數體內賦值則是「賦值」。對於 const、引用成員或無默認構造函數的類成員,初始值列表是唯一選擇;對內置類型亦能避免默認構造後再賦值的開銷。


complex(double r = 0, double i = 0) : re(r), im(i) { }

5. 參數傳遞:優先考慮 const T&

為類類型參數使用 const 引用,避免不必要的拷貝構造與析構開銷。內置類型(char、int 等)值傳遞效率通常更高,但為風格統一,亦可一律採用引用。


complex& operator+=(const complex& rhs); // 避免拷貝,const 保護參數不被修改

6. 返回值:能用引用盡量用引用——但有前提

返回引用可避免拷貝,支持鏈式調用(如 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);

8. 接口前置:public 成員放在類聲明最前面

類的使用者關心的是「能做什麼」,而非「怎麼實現」。將公有接口置頂,閱讀者一眼即見核心功能,實現細節(private 數據、輔助函數)後置。


二、 帶指針成員類的「三大件」:深拷貝與資源管理

當類包含指針成員(如動態分配內存的字符串類 String)時,編譯器默認的淺拷貝會導致雙重釋放、內存洩漏、野指針等嚴重問題。必須顯式實現:

  • 拷貝構造函數
  • 拷貝賦值運算符
  • 析構函數

1. 析構函數:釋放動態資源


String::~String() { delete[] m_data; }

2. 拷貝賦值運算符:經典「檢查自賦值 → 釋放舊 → 分配新 → 深拷貝」四步曲


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;
}

拷貝構造函數同理,只做深拷貝(無需釋放舊資源)。

3. 為什麼必須檢查自賦值?

若 a = a,未檢查則 delete[] m_data 會釋放掉右值也在引用的內存,隨後 strcpy 讀取已釋放內存 → 未定義行為。


三、 static 與類:靜態成員的正確理解與應用

1. 靜態數據成員:屬於類、不屬於對象

銀行利率、對象計數器等「全局共享、單份存儲」的數據應聲明為 static。它們存儲於全局/靜態存儲區,程序啟動時構造,結束時析構。

2. 靜態成員函數:無 this 指針

  • 只能訪問靜態成員(數據/函數)
  • 可通過 對象.函數() 或 類名::函數() 調用
  • 本質是普通全局函數,只是作用域限制在類內

3. 靜態數據成員必須在類外定義


class A { static int a; }; // 類內聲明
int A::a = 10;             // 類外定義(分配內存)

4. 單例模式:餓漢式 vs 懶漢式

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved