国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

C++ 原始指針、shared_ptr、unique_ptr 分別在什么場景下使用?

開發
很多初學者寫C++,一上來就 new 一個對象,然后用裸指針到處傳,最后忘記 delete,內存泄漏滿天飛。作為一個寫了多年C++的老碼農,我必須好好聊聊這三兄弟。

大家好,我是小康。

這個問題太經典了!作為一個寫了多年C++的老碼農,我必須好好聊聊這三兄弟。

先說結論

一句話總結:

  • unique_ptr - 默認選擇,90%的場景用它
  • shared_ptr - 不得已需要共享所有權時才用
  • 原始指針 - 只用于"借用"對象,不表示所有權

一、為什么要搞清楚這個問題?

很多初學者寫C++,一上來就 new 一個對象,然后用裸指針到處傳,最后忘記 delete,內存泄漏滿天飛。

核心問題在于:指針沒有明確表達"所有權"的語義。

看到一個 Widget* ptr,你能回答以下問題嗎?

  • 誰負責刪除這個對象?
  • 這個指針能不能為空?
  • 能不能拷貝這個指針?
  • 對象的生命周期是多長?

答不上來?那就對了,因為裸指針什么信息都不傳達。

而智能指針的價值,就是通過類型系統明確表達所有權語義。

二、unique_ptr - 獨占所有權之王

1. 適用場景

(1) 需要在堆上分配對象,且所有權唯一

// 典型場景:工廠模式
std::unique_ptr<Shape> createShape(ShapeType type) {
    switch(type) {
        case ShapeType::Circle:
            return std::make_unique<Circle>();
        case ShapeType::Square:
            return std::make_unique<Square>();
    }
}

// 調用方獲得唯一所有權
auto shape = createShape(ShapeType::Circle);
shape->draw();  // 離開作用域自動釋放,零成本

(2) 類成員需要管理動態分配的資源

class Application {
private:
    std::unique_ptr<Database> db_;      // 獨占數據庫連接
    std::unique_ptr<Logger> logger_;    // 獨占日志器
    
public:
    Application() 
        : db_(std::make_unique<Database>())
        , logger_(std::make_unique<Logger>()) {}
    
    // 編譯器自動生成的析構函數會正確釋放資源
    // 不需要手寫 ~Application()
};

(3) pImpl 慣用法(指針實現)

// Widget.h
class Widget {
public:
    Widget();
    ~Widget();
    // ...
private:
    class Impl;
    std::unique_ptr<Impl> pImpl_;  // 前向聲明+unique_ptr完美組合
};

(4) 多態對象容器

std::vector<std::unique_ptr<Animal>> zoo;
zoo.push_back(std::make_unique<Dog>());
zoo.push_back(std::make_unique<Cat>());

for(auto& animal : zoo) {
    animal->makeSound();  // 多態調用
}
// vector銷毀時自動釋放所有動物對象

2. 核心優勢

  • 零開銷: 和裸指針一樣大小(8字節),性能無損失
  • 移動語義: 可以通過 std::move 轉移所有權
  • 明確語義: 看到 unique_ptr 就知道是獨占所有權
  • 異常安全: RAII保證即使拋異常也能正確釋放

3. 使用要點

//  推薦:使用 make_unique
auto ptr = std::make_unique<Widget>(arg1, arg2);

//  不推薦:手動new
std::unique_ptr<Widget> ptr(new Widget(arg1, arg2));  
// 可能在異常情況下泄漏

//  轉移所有權
auto ptr2 = std::move(ptr);  // ptr變為nullptr

//  不能拷貝
auto ptr3 = ptr2;  // 編譯錯誤!

三、shared_ptr - 共享所有權專家

1. 適用場景

(1) 多個對象需要共享一個資源的所有權

class Node {
public:
    std::string data;
    std::shared_ptr<Node> next;  // 鏈表可能被多個地方引用
};

// 多個容器共享同一個對象
std::vector<std::shared_ptr<Resource>> active_resources;
std::map<int, std::shared_ptr<Resource>> resource_cache;

auto res = std::make_shared<Resource>();
active_resources.push_back(res);   // 引用計數 = 2
resource_cache[100] = res;         // 引用計數 = 3
// 任何一個容器清理時不會過早刪除對象

(2) 異步編程中,對象需要被多個異步任務共享

void processAsync(std::shared_ptr<Data> data) {
    std::thread t([data]() {
        // 捕獲shared_ptr,確保Data對象在線程執行期間有效
        std::this_thread::sleep_for(std::chrono::seconds(1));
        data->process();
    });
    t.detach();
}

auto data = std::make_shared<Data>();
processAsync(data);
processAsync(data);
// data 會在所有線程完成后才被銷毀

(3) 緩存實現

class ResourceManager {
    std::map<std::string, std::weak_ptr<Resource>> cache_;
    
public:
    std::shared_ptr<Resource> getResource(const std::string& key) {
        auto it = cache_.find(key);
        if (it != cache_.end()) {
            if (auto sp = it->second.lock()) {  // 嘗試提升weak_ptr
                return sp;  // 緩存命中
            }
        }
        
        // 緩存未命中,創建新資源
        auto resource = std::make_shared<Resource>(key);
        cache_[key] = resource;  // 存儲weak_ptr
        return resource;
    }
};

2. 性能開銷

shared_ptr 不是免費的午餐,它有實實在在的開銷:

  • 內存開銷: 16字節(裸指針8字節 + 控制塊指針8字節)
  • 控制塊: 額外的堆分配,包含引用計數、弱引用計數等
  • 原子操作: 引用計數的增減是原子操作,在多線程下有同步開銷
// 性能對比
std::unique_ptr<int> up(new int(10));  // 8字節,無原子操作
std::shared_ptr<int> sp(new int(10));  // 16字節,原子操作

// make_shared 優化:一次分配
auto sp2 = std::make_shared<int>(10);  // 對象和控制塊一起分配

3. 注意事項

(1) 避免循環引用

struct Node {
    std::shared_ptr<Node> next;
    std::shared_ptr<Node> prev;  // 問題:會造成循環引用
};

// 解決方案:用weak_ptr打破循環
struct Node {
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev;  //  不增加引用計數
};

(2) 不要用同一個裸指針初始化多個 shared_ptr

Widget* raw = new Widget();
std::shared_ptr<Widget> sp1(raw);
std::shared_ptr<Widget> sp2(raw);  //  災難!會double-free
// 正確做法:
auto sp1 = std::make_shared<Widget>();
auto sp2 = sp1;  //  共享所有權

四、原始指針 - 觀察者角色

1. 核心理念

原始指針在現代C++中的定位:表示"借用"而非"所有權"。

根據C++ Core Guidelines和Chromium編碼規范的建議:

  • 原始指針 = "我不擁有這個對象,只是臨時使用它"
  • 智能指針 = "我擁有或共享這個對象的所有權"

2. 適用場景

(1) 函數參數 - 只需要訪問對象,不涉及所有權

//  不推薦:傳智能指針
void processData(std::unique_ptr<Data>& data);  
void processData(const std::shared_ptr<Data>& data);

//  推薦:傳裸指針或引用
void processData(Data* data);        // 可以為空
void processData(Data& data);        // 不能為空

// 調用方
auto data = std::make_unique<Data>();
processData(data.get());  // 或 processData(*data);

(2) 觀察者模式

class Subject {
    std::vector<Observer*> observers_;  // 不擁有觀察者
    
public:
    void attach(Observer* obs) {
        observers_.push_back(obs);
    }
    
    void notify() {
        for (auto* obs : observers_) {
            obs->update();
        }
    }
};

(3) 可選的回調

class Worker {
public:
    // 進度回調是可選的,用裸指針表示"我不管理你的生命周期"
    void doWork(ProgressCallback* callback = nullptr) {
        // ...
        if (callback) {
            callback->onProgress(50);
        }
        // ...
    }
};

(4) 臨時的局部指針

void processVector(std::vector<std::unique_ptr<Widget>>& widgets) {
    for (auto& widget : widgets) {
        Widget* raw = widget.get();  // 臨時獲取裸指針
        
        // 在小范圍內使用裸指針,性能更好
        raw->step1();
        raw->step2();
        raw->step3();
    }
}

(5) 與C API交互

// C 庫函數期望裸指針
void legacy_c_function(void* data);

// C++ 代碼
auto data = std::make_unique<MyData>();
legacy_c_function(data.get());  // 必須用裸指針

(6) 性能關鍵路徑

// 游戲引擎的渲染循環
void GameEngine::render() {
    // 每幀調用,用裸指針避免引用計數開銷
    Renderer* renderer = renderer_.get();
    Camera* camera = camera_.get();
    
    for (int i = 0; i < 1000000; ++i) {
        renderer->drawObject(objects_[i], camera);
    }
}

3. 使用原則

根據Microsoft的文檔和實踐經驗:

  • 在現代C++中,原始指針只應該在小的代碼塊、局部作用域、輔助函數中使用,并且要確保性能關鍵且不會造成所有權混淆。

安全使用裸指針的黃金法則:

  • 永遠不要 delete 一個函數參數傳入的裸指針
  • 永遠不要 new 一個對象后用裸指針存儲(直接用智能指針)
  • 如果拿到裸指針,確認原對象的生命周期足夠長
  • 優先用引用代替裸指針(如果不需要表示"可空")
//  好的實踐
void goodPractice(const std::unique_ptr<Data>& owner) {
    Data* borrowed = owner.get();  // 明確:只是借用
    borrowed->process();
    // 不會也不應該 delete borrowed
}

//  壞的實踐
Data* badPractice() {
    Data* data = new Data();  // 誰負責delete?不清楚!
    return data;
}

五、實戰決策樹

給你一個快速決策流程:

需要堆分配對象嗎?
├─ 否 → 用棧對象或引用
└─ 是 ↓

需要共享所有權嗎?
├─ 是 → 用 shared_ptr
│      ├─ 需要打破循環引用? → 配合 weak_ptr
│      └─ 性能敏感? → 重新考慮設計,是否真的需要共享
│
└─ 否 ↓

所有權是否唯一且明確?
├─ 是 → 用 unique_ptr  (90%的情況)
└─ 否 → 你只是想"看看"對象,不擁有它
         └─ 用裸指針或引用
              ├─ 可以為空? → 用 T*
              └─ 不可為空? → 用 T&

六、性能考量

讓我用數據說話(基于C++ Core Guidelines的測試):

// 創建和銷毀的性能對比 (相對時間)
裸指針 new/delete:        1.0x  (基準)
unique_ptr:               1.0x  (幾乎無開銷!)
shared_ptr(分別分配):     3.2x  (new對象 + new控制塊)
make_shared:              2.1x  (一次分配,優化版)

// 拷貝/移動的性能對比
裸指針拷貝:               1.0x
unique_ptr 移動:          1.1x  (極小開銷)
shared_ptr 拷貝:          10.3x (原子操作+緩存一致性)
shared_ptr 移動:          1.2x

結論:

  • unique_ptr 基本是零成本抽象
  • shared_ptr 拷貝開銷大,盡量移動
  • 能用 unique_ptr 就別用 shared_ptr

七、常見誤區

誤區1: "智能指針很慢,性能關鍵代碼要用裸指針"

真相:unique_ptr 編譯后和裸指針完全一樣,零開銷。shared_ptr 的開銷主要在拷貝時的原子操作,如果通過引用傳遞或移動,開銷也很小。

誤區2: "看到指針就用 shared_ptr,保險"

真相: 濫用 shared_ptr 會:

  • 隱藏所有權設計問題
  • 引入不必要的性能開銷
  • 容易造成循環引用
  • 讓代碼更難理解

誤區3: "函數參數用 const unique_ptr& 很安全"

真相: 這樣做強制調用方必須用 unique_ptr,限制了靈活性。如果函數不需要所有權,直接用裸指針或引用。

//  過度設計
void process(const std::unique_ptr<Data>& data);

//  簡單明了
void process(Data* data);  // 或 Data& data

誤區4: "原始指針太危險,應該完全避免"

真相: 裸指針本身不危險,危險的是不明確的所有權。當用裸指針明確表示"借用"時,它是完美的工具。

八、一些建議

  • 默認選擇 unique_ptr - 90%的情況下它就是答案
  • 明確所有權 - 每個堆對象應該有一個明確的owner(unique_ptr或shared_ptr)
  • 借用用裸指針 - 函數參數、觀察者、臨時使用場景
  • 避免過早使用  shared_ptr - 共享所有權往往意味著設計問題
  • 配合 make_unique 和 make_shared - 更安全,更高效
責任編輯:趙寧寧 來源: 跟著小康學編程
相關推薦

2025-06-24 10:00:00

智能指針代碼unique_ptr

2025-12-12 08:25:45

2025-08-14 09:19:48

2025-12-15 06:05:00

C++11多線程shared_ptr

2025-09-15 02:00:00

2025-04-29 08:35:00

2025-02-26 01:23:02

C++11Raw代碼

2025-05-28 08:50:00

C++循環引用節點

2025-05-22 10:10:00

C++循環引用開發

2014-11-03 09:52:25

DNSUDPTCP

2023-11-17 11:48:08

智能指針C++

2021-12-21 15:31:10

C++語言指針

2024-07-03 12:04:42

C++this?

2011-07-20 16:43:34

C++

2018-05-30 15:01:45

語言框架Java

2018-08-10 09:00:50

PythonJavaPHP

2023-09-03 22:46:27

數據庫PostgreSQL

2010-01-26 13:42:28

C++指針

2011-04-11 11:09:50

this指針

2025-10-09 04:00:00

點贊
收藏

51CTO技術棧公眾號

日本在线中文电影| 久久久久久久久久福利| 伊人一区二区三区久久精品| 亚洲卡通动漫在线| 六月婷婷一区| 成人动态视频| 国产午夜精品久久久久免费视| 国产特级淫片高清视频| 国产欧美一区二区三区在线| 亚洲男人的天堂在线播放| 一区二区三区在线免费播放| 久久成人免费网| 天天射成人网| 国产视频一区二区在线播放| 在线免费观看的av网站| 国产aaaaa毛片| 天堂精品一区二区三区| 国产极品jizzhd欧美| 亚洲精品国产精品国产自| 五月天中文字幕一区二区| 不卡av在线免费观看| 精品9999| 九九亚洲视频| 黄色成人在线观看网站| 五月婷婷在线视频| 成全视频全集| 青青青在线视频播放| 国产精品久久久久久免费观看| 欧美激情第一页xxx| 日韩av综合网站| 91极品视觉盛宴| 中文字幕精品一区二区三区精品| 久久99精品国产| 亚洲国产第一| sdde在线播放一区二区| 成人污版视频| 手机av在线| 免费在线毛片网站| 黄页网址在线观看| 欧美日韩一区二区在线免费观看| 日本在线播放不卡| 亚洲一区二区三区在线免费观看| 久久久久久久久国产精品| 日韩国产欧美区| 欧美精品tushy高清| 亚洲一级二级三级| 久久久久久久精| 国产99久久久国产精品潘金网站| 久久九九精品| 精品动漫av| 在线观看日韩| 日韩精品诱惑一区?区三区| 视频欧美一区| 成人午夜亚洲| av日韩国产| caopon在线免费视频| 欧美孕妇性xxxⅹ精品hd| 啊啊啊好爽视频| 性chinese极品按摩| av免费观看网| 可以看毛片的网址| 麻豆传媒网站在线观看| 日韩精品欧美专区| 欧美午夜精品理论片a级大开眼界 欧美午夜精品久久久久免费视 | 国产98在线|日韩| 国产精品精品久久久| 国内精品久久久久久| 欧美精品免费在线| 久久久999国产精品| 一本色道久久88亚洲综合88| 亚洲精品久久久一区二区三区| 欧美日韩成人在线| 欧美网站大全在线观看| 疯狂欧美牲乱大交777| 亚洲国产aⅴ天堂久久| 亚洲综合偷拍欧美一区色| 亚洲欧美中日韩| 日本一区二区三区免费乱视频 | 国产欧美日韩精品一区二区三区| 6080亚洲理论片在线观看| 国产一区二区三区黄网站| 日韩三级成人| 精品三级久久久| 久久国际精品| 美女精品久久| 国产一区二区三区不卡av| 精品精品国产毛片在线看| 免费萌白酱国产一区二区三区| 国产高清精品二区| 日韩一区二区三区在线看| 国产伦精品一区二区三区免费优势| 中文字幕日韩在线| 天海翼亚洲一区二区三区| 精品国产一区二区三区久久久樱花| 激情五月综合网| 免费欧美一区| 日韩欧美国产小视频| 91精品一区二区三区四区| 国产极品人妖在线观看| 欧美在线播放| 日韩精品黄色网| 欧美一区二区三区在线免费观看| 欧美久久一二区| 欧美精品一级二级三级| 国产在线观看91一区二区三区| 色婷婷精品大在线视频| 97视频在线观看免费| 激情婷婷综合网| 日韩免费在线电影| 久久精品国产亚洲aⅴ| 亚洲成人一区二区在线观看| 77777亚洲午夜久久多人| 日日鲁鲁鲁夜夜爽爽狠狠视频97| 亚洲精品少妇久久久久久 | 久久美女福利视频| 极品束缚调教一区二区网站| 欧美三级韩国三级日本一级| 久久人人爽人人爽人人av| 欧美综合影院| 亚洲天堂福利av| 日韩免费观看网站| 日韩精品综合在线| 97se亚洲| 欧美日韩在线播放一区| 激情欧美一区二区三区中文字幕| av女同在线| 91综合视频| 欧美激情视频网址| 毛片在线导航| 成人免费在线视频观看| 国产偷国产偷亚洲高清97cao| 国产中文欧美日韩在线| 狠狠躁夜夜躁人人爽超碰91| 少妇人妻大乳在线视频| 国产中文字幕一区二区三区| 精品精品欲导航| 国产亚洲视频在线观看| av中字幕久久| 天天av综合| www.国产精品一二区| 成全电影播放在线观看国语| 日本高清免费不卡视频| 日韩视频在线免费播放| 精品一区在线看| 99久久无色码| 极品尤物久久久av免费看| 免费欧美一级视频| 欧美日韩一区二区在线播放| 婷婷综合六月| 懂色av一区二区三区在线播放| 成人app下载| 亚洲一二三区av| 国产精品资源在线| 亚洲无吗一区二区三区| 久久亚洲春色中文字幕久久久| 黄色网免费看| 亚洲免费观看在线观看| 国产裸舞福利在线视频合集| 久久综合久久鬼色| 日韩中文字幕三区| 免费国产亚洲视频| 免费男同深夜夜行网站| 4438成人网| 丝袜在线观看| 久久高清视频免费| 亚洲日本三级| 在线观看成人免费| 免费在线日韩av| 国产极品在线视频| 亚洲成人高清在线| 国产1区2区3区在线| 国产美女在线观看| 日韩欧美一区电影| 欧美精品一区二区久久| 亚洲国产91视频| 亚洲精品国产精品粉嫩| 日韩av影院在线观看| 久操视频在线免费播放| 亚洲不卡在线观看| 污版网站在线观看| 欧美国产综合一区二区| 成人免费黄色网址| 国产精品久久久久久久久久久免费看| 人人妻人人澡人人爽欧美一区| 亚洲无毛电影| 国产成人精品福利一区二区三区 | 天天免费综合色| 一区二区三区免费播放| 国产精品色在线观看| 成年美女网站| 亚洲午夜影视影院在线观看| 黄色高清在线观看| 欧美性高潮床叫视频| 国产在线观看www| 欧美不卡123| 久久uomeier| 精品婷婷伊人一区三区三| 欧美精品一区三区在线观看| 日韩精品黄色网| 欧美精品一区二|