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

STL 容器中正確管理物件指標的封裝技巧

     編輯:關於C++

前言

STL 容器設計初衷是存放物件實體(value semantics),而非指標。直接存放指標雖然語法合法,但極易導致記憶體洩漏、雙重釋放、淺拷貝等問題。然而在以下情境,使用指標(或智慧指標)確有必要:

  1. 物件極大,頻繁拷貝成本過高
  2. 同一物件需放入多個容器共享
  3. 容器需存放繼承自同一基類的多型衍生物件

本文介紹一個指標封裝類別,讓 STL 容器能安全管理指標,並支援排序、比較等演算法需求。

一、PtrWrapper 封裝類別設計

核心想法

  • 將原始指標包裝為值型別物件,符合 STL 容器需求
  • 實作拷貝建構、指派、解構,管理指標生命週期(此處示範淺拷貝,實務建議改用智慧指標)
  • 多載比較運算子(==、<、>),支援 set、sort 等演算法
  • 提供隱含轉換或呼叫運算子 () 取得原始指標

程式碼實作

// 通用指標封裝樣板
template <typename T>
class PtrWrapper {
    T* ptr_;
public:
    // 建構/拷貝/指派/解構
    PtrWrapper(T* p = nullptr) : ptr_(p) {}
    PtrWrapper(const PtrWrapper& other) : ptr_(other.ptr_) {}
    PtrWrapper& operator=(const PtrWrapper& other) {
        ptr_ = other.ptr_; return *this;
    }
    ~PtrWrapper() {}  // 注意:此處不刪除指標,屬淺拷貝語意

    // 取得原始指標
    T* get() const { return ptr_; }
    T* operator->() const { return ptr_; }
    T& operator*() const { return *ptr_; }

    // 比較運算子(委派給 T 的運算子)
    bool operator==(const PtrWrapper& rhs) const {
        return ptr_ && rhs.ptr_ ? *ptr_ == *rhs.ptr_ : ptr_ == rhs.ptr_;
    }
    bool operator<(const PtrWrapper& rhs) const {
        return ptr_ && rhs.ptr_ ? *ptr_ < *rhs.ptr_ : ptr_ < rhs.ptr_;
    }
    bool operator>(const PtrWrapper& rhs) const { return rhs < *this; }
};

二、被管理物件需滿足的條件

為了讓 PtrWrapper 的比較運算子正常運作,被指向的類別 T 必須實作:

  • 拷貝建構子、指派運算子、解構子(三大件)
  • operator==、operator< 等比較運算子
class Data {
    int id_;
public:
    Data(int id) : id_(id) {}
    Data(const Data& d) : id_(d.id_) {}
    Data& operator=(const Data& d) { id_ = d.id_; return *this; }
    ~Data() {}
    bool operator==(const Data& rhs) const { return id_ == rhs.id_; }
    bool operator<(const Data& rhs) const { return id_ < rhs.id_; }
    int getId() const { return id_; }
};

三、實戰應用範例

#include <list>
#include <set>
#include <deque>
#include <algorithm>
#include <iostream>

void print(const PtrWrapper<Data>& pw) {
    std::cout << pw->getId() << ' ';
}

int main() {
    // 1. 建立指標陣列
    PtrWrapper<Data> arr[5];
    for (int i = 0; i < 5; ++i)
        arr[i] = PtrWrapper<Data>(new Data(i * i));  // 動態配置

    // 2. 打亂順序
    std::random_shuffle(arr, arr + 5);

    // 3. 複製到 list
    std::list<PtrWrapper<Data>> lst;
    std::copy(arr, arr + 5, std::back_inserter(lst));
    std::for_each(lst.begin(), lst.end(), print); std::cout << '\n';

    // 4. 複製到 set(自動排序、去重)
    std::set<PtrWrapper<Data>, std::greater<PtrWrapper<Data>>> st;
    std::copy(lst.begin(), lst.end(), std::inserter(st, st.begin()));
    std::for_each(st.begin(), st.end(), print); std::cout << '\n';

    // 5. 複製到 deque
    std::deque<PtrWrapper<Data>> dq;
    std::copy(lst.begin(), lst.end(), std::back_inserter(dq));
    std::for_each(dq.begin(), dq.end(), print); std::cout << '\n';

    // 6. 釋放記憶體(PtrWrapper 不負責刪除,需手動管理)
    for (int i = 0; i < 5; ++i) delete arr[i].get();
    return 0;
}

四、進階議題與現代替代方案

1. 記憶體管理責任

上述 PtrWrapper 採用淺拷貝,不負責刪除指標。實務上必須明確擁有權歸屬,或改用:

  • std::shared_ptr<T>(C++11):共享擁有權,引用計數自動釋放
  • std::unique_ptr<T>(C++11):獨佔擁有權,需搭配移動語意存入容器
  • boost::ptr_container:專為指標設計的容器,自動管理生命週期

2. 多型容器

存放基類指標(PtrWrapper<Base>)可實現異質容器,但需注意:

  • 基類需有虛擬解構子
  • 比較運算子需正確處理動態型別(或改用自定比較函式)
  • C++11 後建議直接用 std::vector<std::unique_ptr<Base>>

3. 效能考量

BCB TList 超過 5000 元素效能明顯下降且非型別安全,STL 容器配合封裝類別可同時獲得型別安全、跨平台可攜性與優異效能。

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