成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

我用 shared_ptr 踩了三年坑,終于明白 Google 為什么推薦 unique_ptr

開發
智能指針的"智能"不在于功能有多復雜,而在于設計思路的清晰和使用場景的準確。就像寫代碼一樣,簡單往往比復雜更難,但也更有價值。

大家好,我是小康。

最近在技術群里看到一個有趣的討論,一個小伙伴問:"為什么Google的代碼規范總是推薦用unique_ptr,對shared_ptr卻很謹慎?是不是shared_ptr有什么坑?"

這個問題瞬間炸出了一群技術大佬,有人說shared_ptr性能差,有人說容易內存泄漏,還有人說設計思路就不對...

說實話,我剛開始學C++的時候也很困惑。明明shared_ptr看起來更"智能"啊,可以自動管理引用計數,多個對象可以共享,聽起來就很高級。而unique_ptr好像就是個"獨占狂",一個對象只能有一個主人,顯得很"小氣"。

但是工作幾年后,我終于明白了其中的門道。今天就來給大家深度剖析一下這兩個"智能指針兄弟"的恩怨情仇。

一、先說說這兩兄弟的"出身"

1. unique_ptr:獨占型的"專一男友"

unique_ptr就像那種專一的男朋友,一旦認定了一個對象,就獨占所有權,絕不與其他人分享。

std::unique_ptr<int> ptr1(new int(42));
// std::unique_ptr<int> ptr2 = ptr1;  // 編譯錯誤!不能復制
std::unique_ptr<int> ptr2 = std::move(ptr1);  // 只能轉移所有權
// 現在ptr1為空,ptr2擁有對象

2. shared_ptr:共享型的"中央空調"

shared_ptr則像中央空調,可以同時服務多個"用戶",內部維護一個引用計數器。

std::shared_ptr<int> ptr1(new int(42));
std::shared_ptr<int> ptr2 = ptr1;  // 可以復制,引用計數變為2
std::shared_ptr<int> ptr3 = ptr1;  // 引用計數變為3
// 當所有shared_ptr都銷毀后,對象才會被釋放

二、為什么Google偏愛unique_ptr?

1. 性能差距:不是一個量級的

我們先來看看性能對比,數據會說話:

// 性能測試代碼
#include <chrono>
#include <memory>

// unique_ptr創建和銷毀
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000000; ++i) {
    auto ptr = std::make_unique<int>(i);
}  // 自動銷毀
auto end = std::chrono::high_resolution_clock::now();
std::cout << "unique_ptr用時: " << 
    std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() 
    << "微秒" << std::endl;

// shared_ptr創建和銷毀
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000000; ++i) {
    auto ptr = std::make_shared<int>(i);
}  // 自動銷毀
end = std::chrono::high_resolution_clock::now();
std::cout << "shared_ptr用時: " << 
    std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() 
    << "微秒" << std::endl;

在我的機器上測試結果:

  • unique_ptr:約20072微秒
  • shared_ptr:約60081微秒

shared_ptr竟然比unique_ptr慢了3倍多!

為什么會這樣?因為shared_ptr需要:

  • 維護引用計數(原子操作,線程安全但開銷大)
  • 額外的內存分配(控制塊)
  • 復制時需要原子遞增
  • 銷毀時需要原子遞減并檢查是否為0

2. 內存開銷:shared_ptr是個"大胃王"

std::cout << "unique_ptr大小: " << sizeof(std::unique_ptr<int>) << "字節" << std::endl;
std::cout << "shared_ptr大小: " << sizeof(std::shared_ptr<int>) << "字節" << std::endl;

結果:

  • unique_ptr:8字節(就是一個普通指針)
  • shared_ptr:16字節(指針+控制塊指針)

而且shared_ptr還會額外分配一個控制塊,存儲引用計數等信息,至少需要額外的16個字節。

如果你的程序中有大量的智能指針,這個差距就很明顯了。

3. 設計哲學:ownership要清晰

Google的代碼規范有一個重要原則:所有權要清晰。

看這個例子:

// 不好的設計:所有權不明確
class DataProcessor {
    std::shared_ptr<Data> data_;
public:
    void setData(std::shared_ptr<Data> data) { data_ = data; }
};

class DataCache {
    std::shared_ptr<Data> cached_data_;
public:
    void cache(std::shared_ptr<Data> data) { cached_data_ = data; }
};

// 使用時:誰擁有data?誰負責生命周期?不清楚!
auto data = std::make_shared<Data>();
processor.setData(data);
cache.cache(data);
// 好的設計:所有權清晰
class DataManager {
    std::unique_ptr<Data> data_;  // 明確的擁有者
public:
    Data* getData() { return data_.get(); }  // 只提供訪問權
    void setData(std::unique_ptr<Data> data) { 
        data_ = std::move(data); 
    }
};

// 使用時:DataManager明確擁有data
auto data = std::make_unique<Data>();
manager.setData(std::move(data));

三、shared_ptr的"隱形炸彈"

1. 循環引用:程序員的噩夢

這是shared_ptr最著名的坑:

class Parent;
class Child;

class Parent {
public:
    std::shared_ptr<Child> child_;
    ~Parent() { std::cout << "Parent析構" << std::endl; }
};

class Child {
public:
    std::shared_ptr<Parent> parent_;  // 危險!循環引用
    ~Child() { std::cout << "Child析構" << std::endl; }
};

{
    auto parent = std::make_shared<Parent>();
    auto child = std::make_shared<Child>();
    
    parent->child_ = child;
    child->parent_ = parent;  // 形成循環引用
}
// 程序結束,但你會發現析構函數都沒被調用!
// 內存泄漏了!

這種bug特別隱蔽,很難調試。而unique_ptr從設計上就避免了這個問題。

2. 線程安全的假象

很多人以為shared_ptr是線程安全的,其實只是引用計數的操作是線程安全的,對象本身的訪問并不安全:

std::shared_ptr<std::vector<int>> ptr = std::make_shared<std::vector<int>>();

// 這些操作是線程安全的:
std::shared_ptr<std::vector<int>> ptr2 = ptr;  // 拷貝shared_ptr
ptr.reset();  // 重置shared_ptr
auto count = ptr.use_count();  // 查看引用計數

// 這些操作不是線程安全的:
// 線程1
ptr->push_back(1);  // 不安全! 修改vector內容

// 線程2  
ptr->push_back(2);  // 不安全!

// 引用計數的增減是安全的,但對vector的操作不安全

3. 性能陷阱:不經意的拷貝

// 看似無害的代碼
void processData(std::shared_ptr<Data> data) {  // 注意:按值傳遞
    // 處理數據...
}  // 函數結束時,局部的shared_ptr被銷毀,引用計數-1

// 每次調用都會發生什么?
auto data = std::make_shared<Data>();  // 引用計數=1
for (int i = 0; i < 1000000; ++i) {
    processData(data);  // 1. 拷貝shared_ptr,引用計數+1(原子操作)
                        // 2. 函數執行
                        // 3. 函數結束,引用計數-1(原子操作)
}
// 100萬次原子操作的開銷!

應該改為:

// 方案1:按引用傳遞shared_ptr(推薦)
void processData(const std::shared_ptr<Data>& data) {
    // 處理數據,無拷貝開銷
}

// 方案2:傳遞原始指針(如果不需要延長生命周期)
void processData(Data* data) {
    // 處理數據...
}

// 方案3:傳遞引用(如果確定對象存在)
void processData(const Data& data) {
    // 處理數據...
}

四、什么時候才用shared_ptr?

雖然我們一直在"黑"shared_ptr,但它確實有適用場景,關鍵是要真正需要共享所有權:

(1) 緩存系統:多個持有者,不確定誰先釋放

class ResourceCache {
    std::unordered_map<std::string, std::shared_ptr<ExpensiveResource>> cache_;
public:
    std::shared_ptr<ExpensiveResource> get(const std::string& key) {
        if (cache_.find(key) == cache_.end()) {
            cache_[key] = std::make_shared<ExpensiveResource>(key);
        }
        return cache_[key];  // 調用者和緩存都持有,誰都可能先釋放
    }
    
    void cleanup() {
        // 清理緩存,但如果外部還在使用,對象不會被銷毀
        cache_.clear();
    }
};

(2) 異步編程:延長對象生命周期

class DataProcessor {
public:
    void processAsync(std::shared_ptr<Data> data) {
        // 啟動異步任務,data的生命周期不確定
        std::thread([data]() {
            std::this_thread::sleep_for(std::chrono::seconds(5));
            // 即使調用方已經結束,data依然有效
            processData(*data);
        }).detach();
    }
    
    // 如果用unique_ptr會怎樣?
    void processBad(std::unique_ptr<Data> data) {
        std::thread([&data]() {  // 危險!引用可能懸空
            processData(*data);   // 可能崩潰
        }).detach();
    }
};

(3) 資源池:共享昂貴資源

class DatabaseConnectionPool {
    std::vector<std::shared_ptr<Connection>> pool_;
public:
    std::shared_ptr<Connection> getConnection() {
        if (!pool_.empty()) {
            auto conn = pool_.back();
            pool_.pop_back();
            return conn;  // 多個客戶端可能同時使用同一連接
        }
        returnstd::make_shared<Connection>();
    }
    
    void returnConnection(std::shared_ptr<Connection> conn) {
        // 簡單地放回池中,shared_ptr會自動管理生命周期
        // 即使有其他地方還在使用這個連接,也沒關系
        // 當所有引用都釋放后,連接會自動銷毀
        pool_.push_back(conn);
    }
};

(4) 插件系統:多模塊共享

class PluginManager {
    std::unordered_map<std::string, std::shared_ptr<Plugin>> plugins_;
public:
    std::shared_ptr<Plugin> loadPlugin(const std::string& name) {
        if (plugins_.find(name) == plugins_.end()) {
            plugins_[name] = std::make_shared<Plugin>(name);
        }
        return plugins_[name];  // 多個模塊可能同時需要同一個插件
    }
};

// 使用場景
auto audioPlugin = pluginManager.loadPlugin("audio");
auto videoPlugin = pluginManager.loadPlugin("audio");  // 返回同一個實例
// audioPlugin和videoPlugin指向同一對象,任何一個都可以安全使用

五、實戰指南:如何選擇智能指針?

遵循這個簡單的決策流程:

第一步:默認選擇unique_ptr

除非有特殊需求,否則總是從unique_ptr開始。它性能最好,語義最清晰。

第二步:遇到問題時再考慮升級

(1) 需要轉移所有權? → 繼續用unique_ptr + std::move

(2) 需要多個擁有者? → 問自己三個問題:

  • 是否真的有多個"擁有者"需要這個對象?
  • 這些擁有者的生命周期是否不確定?
  • 任何一個擁有者都不應該單獨決定對象何時銷毀?

如果三個答案都是"是",才用shared_ptr

(3) 可能有循環引用? → 用weak_ptr打破循環

六、總結:智能指針的"智慧"選擇

Google推薦優先使用unique_ptr不是沒有道理的:

  • 性能更好:沒有引用計數開銷
  • 內存更省:只有一個指針的大小
  • 設計更清晰:明確的所有權語義
  • 更安全:避免循環引用等陷阱

記住這個原則:能用unique_ptr就不用shared_ptr,能用引用就不用指針。

最后想說,智能指針的"智能"不在于功能有多復雜,而在于設計思路的清晰和使用場景的準確。就像寫代碼一樣,簡單往往比復雜更難,但也更有價值。

責任編輯:趙寧寧 來源: 跟著小康學編程
相關推薦

2025-05-28 08:50:00

C++循環引用節點

2025-04-29 08:35:00

2025-05-22 10:10:00

C++循環引用開發

2019-09-09 08:28:48

互聯網數據磁盤

2019-10-30 15:35:47

Android谷歌手機

2010-11-12 09:51:43

Android

2019-08-28 16:38:49

finalJava編程語言

2025-02-26 01:23:02

C++11Raw代碼

2020-04-25 20:20:28

蘋果庫克手機

2011-12-23 10:23:45

GoogleMozilla

2020-09-03 07:55:02

并發操作系統Java

2022-05-17 14:28:42

編程語言Julia代碼

2021-02-18 07:55:27

數據湖存儲數據

2024-07-02 17:43:26

2022-06-28 22:27:38

數字化轉型

2019-09-20 13:16:22

手機攝像頭三攝

2022-01-09 23:44:14

5G4G手機

2022-11-27 17:21:04

ClickHouseJDBC函數

2013-07-17 09:13:19

2016-01-08 10:32:48

虛擬現實頭盔
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产亚洲精品精品国产亚洲综合 | 欧美三级在线 | 天天草天天爱 | 欧一区| 久久噜噜噜精品国产亚洲综合 | 成人在线看片 | www.久草 | 久久久久av | 日本a在线| 自拍偷拍3p | 欧美成人精品一区二区男人看 | 日韩欧美不卡 | 中文字幕1区 | 精品国产一区二区国模嫣然 | 欧美日韩精品中文字幕 | 亚洲精品欧美 | 精品视频久久久久久 | 中文字幕亚洲欧美 | 毛片综合 | 国产中文字幕网 | 一区二区三区四区免费在线观看 | 一区二区三区av | www.国产91| 国产成人艳妇aa视频在线 | 精品在线一区 | 黄色播放 | 一区二区在线视频 | 免费日本视频 | 欧美性受| 97国产精品视频人人做人人爱 | 欧美日韩在线精品 | 91看片网 | 992人人草 | 拍拍无遮挡人做人爱视频免费观看 | 天天干人人 | 第四色播日韩第一页 | 精品乱码一区二区 | 在线看av的网址 | 欧美精品电影一区 | 一级黄大片| 国产精品成人av |