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

Effective C++ 條款 44:從模板中分離參數無關代碼——消除代碼膨脹

     編輯:關於C++

模板的代價:隱性代碼膨脹

模板節省原始碼,但每個實例化產生獨立二進位代碼。非型別參數 (如大小) 或型別參數 (如 int/long 同樣佈局) 導致近似代碼重複,造成 Code Bloat:目標檔膨脹、指令快取局部性變差。

非型別參數膨脹:以矩陣為例


template<typename T, std::size_t n>
class SquareMatrix {
public:
    void invert(); // 5x5 與 10x10 產生兩份幾乎相同代碼
};

解法:提取參數無關代碼至基類


template<typename T>
class SquareMatrixBase {
protected:
    void invert(std::size_t matrixSize); // 共享單一實作
};

template<typename T, std::size_t n>
class SquareMatrix : private SquareMatrixBase<T> {
    using SquareMatrixBase<T>::invert; // 避免隱藏
public:
    void invert() { this->invert(n); } // 內聯呼叫基類版本
};
  • 基類僅參數化 T,所有同 T 不同 n 共享 invert。
  • private 繼承表達「為實現而繼承」非 is-a。
  • this-> 解決模板基類名稱查找問題。

資料存取問題:基類如何知道矩陣資料位置?

方案:基類儲存指標與大小,衍生類決定配置策略 (陣列成員或堆上):


template<typename T>
class SquareMatrixBase {
protected:
    SquareMatrixBase(std::size_t n, T* pMem) : size(n), pData(pMem) {}
    void setDataPtr(T* ptr) { pData = ptr; }
private:
    std::size_t size;
    T* pData;
};

// 陣列成員版 (無動態配置,物件較大)
template<typename T, std::size_t n>
class SquareMatrix : private SquareMatrixBase<T> {
public:
    SquareMatrix() : SquareMatrixBase<T>(n, data) {}
private:
    T data[n*n];
};

// 堆上版 (物件小,需管理生命週期)
template<typename T, std::size_t n>
class SquareMatrix : private SquareMatrixBase<T> {
public:
    SquareMatrix() : SquareMatrixBase<T>(n, 0), pData(new T[n*n]) {
        this->setDataPtr(pData.get());
    }
private:
    boost::scoped_array<T> pData;
};

型別參數膨脹:int/long、指標型別

許多平台 int/long 位元寬度相同,vector<int> 與 vector<long> 成員函式代碼相同。所有指標型別二進位表示相同。解法:

  • 連結器合併相同代碼 (COMDAT folding)。
  • 庫實現將強型別指標操作轉發至 void* 通用實作 (如標準庫 vector/list/deque)。

權衡:代碼大小 vs 執行速度

  • 大小獨立版本:編譯期常數傳播、立即數嵌入,生成更優機器碼。
  • 共享版本:減小執行檔、工作集、改善指令快取局部性,可能整體更快。
  • 唯有實測 (特定平台、典型資料) 才能定奪。

物件大小代價

基類引入指標成員會增加每個物件大小。若衍生類已有資料指標,產生冗餘。可重新設計消除,但會降低封裝性、複雜化資源管理 (指標是否需刪除?)。有時少量代碼重複反而更簡潔。

核心心法

  • 模板產生多類別/函式,參數無關代碼導致膨脹。
  • 非型別參數膨脹 → 以函式參數或資料成員取代模板參數。
  • 型別參數膨脹 → 讓二進位表示相同的實例化共享實作 (轉發至 void* 層)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved