最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++ 智能指針使用不當導致內存泄漏問題解析

 更新時間:2024年07月08日 10:40:15   作者:**K  
這篇文章主要介紹了C++ 智能指針使用不當導致內存泄漏問題解析,本文通過代碼示例給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧

shared_ptr相互嵌套導致循環(huán)引用

代碼示例

#include <iostream>
#include <memory>
using namespace std;
class B;
class A {
public:
    std::shared_ptr<B> b_ptr;
    ~A() { std::cout << "A destroyed\n"; }
};
class B {
public:
    std::shared_ptr<A> a_ptr;
    ~B() { std::cout << "B destroyed\n"; }
};
int main() {
    // 創(chuàng)建 shared_ptr 對象
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    // 相互引用
    a->b_ptr = b;
    b->a_ptr = a;
    cout<<"use_count of a:"<<a.use_count()<<endl;
    cout<<"use_count of b:"<<b.use_count()<<endl;
    return 0;
}

解釋說明

  • 創(chuàng)建了兩個 std::shared_ptr 對象 a 和 b
  • a 持有 b 的 shared_ptr,b 持有 a 的 shared_ptr
  • 當 main 函數結束時,a 和 b 的引用計數不會減少到零,因此它們的析構函數不會被調用。
  • 導致內存泄漏,因為對象 A 和 B 的內存不會被釋放。  

解決方法

為了避免這種循環(huán)引用的問題,可以使用 std::weak_ptrstd::weak_ptr 是一種弱智能指針,它不會增加對象的引用計數。它可以用來打破循環(huán)引用,從而防止內存泄漏。

#include <iostream>
#include <memory>
using namespace std;
class B;  // 先聲明類 B,使得 A 和 B 可以互相引用。
class A {
public:
    std::shared_ptr<B> b_ptr; // A 擁有 B 的強引用
    ~A() { std::cout << "A destroyed\n"; }
};
class B {
public:
    std::weak_ptr<A> a_ptr; // B 擁有 A 的弱引用
    ~B() { std::cout << "B destroyed\n"; }
    void safeAccess() {
        // 嘗試鎖定 a_ptr 獲取 shared_ptr
        if (auto a_shared = a_ptr.lock()) {
            // 安全訪問 a_shared 對象
            std::cout << "Accessing A from B\n";
        } else {
            std::cout << "A is already destroyed, cannot access A from B\n";
        }
    }
};
int main() {
    // 創(chuàng)建 shared_ptr 對象
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    // 互相引用
    a->b_ptr = b;
    b->a_ptr = a;
    // 安全訪問
    b->safeAccess();
    cout<<"use_count of a:"<<a.use_count()<<endl;
    cout<<"use_count of b:"<<b.use_count()<<endl;
    return 0; // 在這里,a 和 b 的引用計數將會正確地減少到零,并且它們將會被銷毀。
}

shared_ptr的層次使用沒有導致循環(huán)引用

shared_ptr<vector<shared_ptr<pair<string, shared_ptr<string>>>>> jsFiles;

這個聲明表示 jsFiles 是一個 std::shared_ptr,它指向一個 std::vector,向量中的每個元素是一個 std::shared_ptr,指向一個 std::pair 對象,而這個 std::pair 對象中包含一個 std::string 和一個 std::shared_ptr<std::string>。它們之間只是層次結構,沒有跨層次的相互引用 。也就是說沒有內存泄漏的問題。證明如下:

#include <iostream>
#include <vector>
#include <memory>
#include <string>
using namespace std;
// 自定義 String 類,模擬 std::string
class MyString {
public:
    std::string data;
    MyString(const std::string& str) : data(str) {
        std::cout << "MyString created: " << data << std::endl;
    }
    ~MyString() {
        std::cout << "MyString destroyed: " << data << std::endl;
    }
    // 添加輸出操作符重載
    friend std::ostream& operator<<(std::ostream& os, const MyString& myStr) {
        os << myStr.data;
        return os;
    }
};
// 自定義 Pair 類,模擬 std::pair
template<typename K, typename V>
class MyPair {
public:
    K first;
    V second;
    MyPair(const K& key, const V& value) : first(key), second(value) {
        std::cout << "MyPair created: {" << first << ", " << *second << "}" << std::endl;
    }
    ~MyPair() {
        std::cout << "MyPair destroyed: {" << first << ", " << *second << "}" << std::endl;
    }
};
int main() {
    // 創(chuàng)建 jsFiles,它是一個 shared_ptr,指向 vector
    auto jsFiles = std::make_shared<std::vector<std::shared_ptr<MyPair<std::string, std::shared_ptr<MyString>>>>>();
    // 添加元素
    auto innerPair1 = std::make_shared<MyPair<std::string, std::shared_ptr<MyString>>>("file1", std::make_shared<MyString>("content of file1"));
    auto innerPair2 = std::make_shared<MyPair<std::string, std::shared_ptr<MyString>>>("file2", std::make_shared<MyString>("content of file2"));
    jsFiles->push_back(innerPair1);
    jsFiles->push_back(innerPair2);
    // 訪問元素
    for (const auto& pairPtr : *jsFiles) {
        std::cout << "Filename: " << pairPtr->first << ", Content: " << *pairPtr->second << std::endl;
    }
    // 離開作用域時,智能指針會自動銷毀它們管理的對象
    return 0;
}

同時也證明了一個結論,構造函數和析構函數的調用順序是相反的。 

回調函數中的循環(huán)引用問題

值捕獲

#include <iostream>
#include <memory>
#include <functional>
class MyClass {
public:
    MyClass() { std::cout << "MyClass created" << std::endl; }
    ~MyClass() { std::cout << "MyClass destroyed" << std::endl; }
    void setCallback(std::function<void()> cb) {
        callback_ = cb;
    }
    void executeCallback() {
        if (callback_) {
            callback_();
        }
    }
private:
    std::function<void()> callback_;
};
void createNoLeak() {
    auto myObject = std::make_shared<MyClass>();
    myObject->setCallback([=]() {
        std::cout << "Callback executed, myObject use count: " << myObject.use_count() << std::endl;
    });
    myObject->executeCallback();
}
int main() {
    createNoLeak();
    std::cout << "End of program" << std::endl;
    return 0;
}

可以看出myObject最后沒有調用析構函數,是shared_ptr循環(huán)引用了。

引用捕獲

如果換為引用捕獲,則不會造成 shared_ptr循環(huán)引用。雖然這種方式不會增加引用計數,但需要特別注意捕獲對象的生命周期,防止在 lambda 被調用時,對象已經被銷毀,從而導致未定義行為。

如何解決 

#include <iostream>
#include <memory>
#include <functional>
class MyClass : public std::enable_shared_from_this<MyClass> {
public:
    MyClass() { std::cout << "MyClass created" << std::endl; }
    ~MyClass() { std::cout << "MyClass destroyed" << std::endl; }
    void setCallback(std::function<void()> cb) {
        callback_ = cb;
    }
    void executeCallback() {
        if (callback_) {
            callback_();
        }
    }
private:
    std::function<void()> callback_;
};
void createNoLeak() {
    auto myObject = std::make_shared<MyClass>();
    std::weak_ptr<MyClass> weakPtr = myObject;
    myObject->setCallback([weakPtr]() {
        if (auto sharedPtr = weakPtr.lock()) {
            std::cout << "Callback executed, object is valid" << std::endl;
        } else {
            std::cout << "Object already destroyed" << std::endl;
        }
    });
    myObject->executeCallback();
    // 這里 myObject 是按 weak_ptr 捕獲,當 createNoLeak() 結束時,myObject 的生命周期也就結束了,并且引用計數=0
}
int main() {
    createNoLeak();
    std::cout << "End of program" << std::endl;
    return 0;
}

  • weakPtr.lock() 的使用:持有 std::weak_ptr,并且需要檢查或者使用其管理的對象。如果對象仍然存在(即它的 shared_ptr 引用計數大于零),我們希望獲取一個 shared_ptr 來安全地使用該對象。否則,weak_ptr.lock() 返回一個空的 shared_ptr。
  • std::enable_shared_from_this 是一個非常有用的標準庫模板類,用于解決一個特定的問題: 當一個類的成員函數需要創(chuàng)建一個指向自己(this)的 std::shared_ptr 時,這類問題如何安全地實現。 std::enable_shared_from_this

背景問題

在使用 std::shared_ptr 管理對象時,有時會遇到需要在類的成員函數中獲取該對象的 shared_ptr 的情況。例如,在一個類的成員函數中,如果想要得到一個指向該對象的 shared_ptr,不能簡單地使用 std::shared_ptr<MyClass>(this),因為這會創(chuàng)建一個新的 shared_ptr,而不是增加現有的 shared_ptr 的引用計數。這可能導致對象被提前銷毀或者多次銷毀。

std::enable_shared_from_this 的作用

通過繼承 std::enable_shared_from_this,類就能夠安全地使用 shared_from_this 方法,從而獲取一個 shared_ptr,該 shared_ptr 與其他 shared_ptr 共享所有權,而不會重復增加引用計數。

使用示例

#include <iostream>
#include <memory>
// 定義 MyClass 繼承 std::enable_shared_from_this<MyClass>
class MyClass : public std::enable_shared_from_this<MyClass> {
public:
    MyClass() { std::cout << "MyClass created" << std::endl; }
    ~MyClass() { std::cout << "MyClass destroyed" << std::endl; }
    // 一個成員函數,它需要返回一個指向自身的 shared_ptr
    std::shared_ptr<MyClass> getSharedPtr() {
        // 使用 shared_from_this 返回一個 shared_ptr
        return shared_from_this();
    }
    void doSomething() {
        auto ptr = shared_from_this(); // 獲取 shared_ptr
        std::cout << "Doing something with MyClass instance, ref count: " << ptr.use_count() << std::endl;
    }
};
void exampleFunction() {
    // 創(chuàng)建 MyClass 對象的 shared_ptr
    auto myObject = std::make_shared<MyClass>();
    // 調用成員函數獲取 shared_ptr
    auto mySharedPtr = myObject->getSharedPtr();
    std::cout << "Reference count after getSharedPtr: " << mySharedPtr.use_count() << std::endl;
    myObject->doSomething();
}
int main() {
    exampleFunction();
    return 0;
}

注意

1.創(chuàng)建對象:
只有通過 std::shared_ptr 創(chuàng)建或管理的對象,才能安全地使用 shared_from_this。

2. 保護避免使用 new 操作符:
直接使用 new 操作符創(chuàng)建的對象不能正確使用 shared_from_this,這樣做可能會導致未定義行為(例如崩潰)。

為什么 std::enable_shared_from_this 是必要的?

std::enable_shared_from_this 內部維護了一個弱引用(std::weak_ptr)指向當前對象。這個弱引用確保不會增加引用計數,同時允許 shared_from_this 方法安全地獲取 std::shared_ptr,從而真正共享管理的對象,避免不安全的重復引用計數增加。

通過這樣做,C++ STL 提供了一種方便而安全的方式來管理對象的生命周期,特別是在需要從對象內部生成 shared_ptr的情境下。

總結

通過繼承 std::enable_shared_from_this,MyClass 能夠安全地在其成員函數中創(chuàng)建返回指向自身的 std::shared_ptr,避免不必要的重復引用計數,從而有效地管理和共享對象生命周期。這樣既提升了代碼的安全性,也使得對象生命周期管理變得更加簡潔和直觀。

到此這篇關于C++ 智能指針使用不當導致內存泄漏問題的文章就介紹到這了,更多相關C++ 智能指針內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C語言深入講解指針與結構體的使用

    C語言深入講解指針與結構體的使用

    指針提供了對地址操作的一種方法,因此,使用指針可使得C語言能夠更高效地實現對計算機底層硬件的操作。另外,通過指針可以更便捷地操作數組。C數組允許定義可存儲相同類型數據項的變量,結構是C編程中另一種用戶自定義的可用的數據類型,它允許您存儲不同類型的數據項
    2022-05-05
  • C語言字符串大小比較

    C語言字符串大小比較

    本文給大家分享給大家的是C語言的字符串大小比較的函數,有需要的小伙伴可以參考下。
    2015-07-07
  • C++實現LeetCode(155.最小棧)

    C++實現LeetCode(155.最小棧)

    這篇文章主要介紹了C++實現LeetCode(155.最小棧),本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下
    2021-07-07
  • 構造函數定義為private或者protected的好處

    構造函數定義為private或者protected的好處

    從語法上來講,一個函數被聲明為protected或者private,那么這個函數就不能從“外部”直接被調用了。對于protected的函數,子類的“內部”的其他函數可以調用之。而對于private的函數,只能被本類“內部”的其他函數說調用
    2013-10-10
  • QT5.12.5移植到ARM平臺下的方法步驟

    QT5.12.5移植到ARM平臺下的方法步驟

    本文主要介紹了QT5.12.5移植到ARM平臺下的方法步驟,包括修改配置文件、代碼修改以及測試運行,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-01-01
  • C++中std::shuffle的使用小結

    C++中std::shuffle的使用小結

    std::shuffle是C++標準庫中的一個函數,用于對容器中的元素進行隨機排列,本文主要介紹了C++中std::shuffle的使用小結,具有一定的參考價值,感興趣的可以了解一下
    2025-04-04
  • C語言實現進制轉換函數的實例詳解

    C語言實現進制轉換函數的實例詳解

    這篇文章主要介紹了C語言實現進制轉換函數的實例詳解的相關資料,這里提供實現實例幫助大家實現改功能,需要的朋友可以參考下
    2017-08-08
  • C++實現String與UF8互轉

    C++實現String與UF8互轉

    這篇文章介紹了C++實現String與UF8互轉的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-05-05
  • c語言如何輸出一個n行m列的圖形

    c語言如何輸出一個n行m列的圖形

    這篇文章主要介紹了c語言如何輸出一個n行m列的圖形問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • C語言詳細分析結構體的內存對齊規(guī)則

    C語言詳細分析結構體的內存對齊規(guī)則

    C 數組允許定義可存儲相同類型數據項的變量,結構是 C 編程中另一種用戶自定義的可用的數據類型,它允許你存儲不同類型的數據項,本篇讓我們來了解C 的結構體內存對齊
    2022-07-07

最新評論

英山县| 嫩江县| 治县。| 綦江县| 开平市| 盈江县| 岑溪市| 农安县| 银川市| 沙洋县| 榆林市| 阜城县| 花莲县| 青海省| 凤山市| 永安市| 女性| 安陆市| 桃江县| 永昌县| 壤塘县| 西乌珠穆沁旗| 宣化县| 石嘴山市| 日土县| 通海县| 通河县| 靖安县| 花莲市| 裕民县| 尉氏县| 甘德县| 紫金县| 沙湾县| 通许县| 维西| 云安县| 门源| 上思县| 蒙自县| 调兵山市|