亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++共享智能指針shared_ptr的實現(xiàn)

 更新時間:2023年12月28日 10:17:50   作者:Liuuuu408  
在C++中沒有垃圾回收機制,必須自己釋放分配的內(nèi)存,否則就會造成內(nèi)存泄露,解決這個問題最有效的方法是使用智能指針,本文主要介紹了C++共享智能指針shared_ptr的實現(xiàn),感興趣的可以了解一下

共享智能指針

在C++中沒有垃圾回收機制,必須自己釋放分配的內(nèi)存,否則就會造成內(nèi)存泄露。解決這個問題最有效的方法是使用智能指針(smart pointer)。智能指針是存儲指向動態(tài)分配(堆)對象指針的類,用于生存期的控制,能夠確保在離開指針?biāo)谧饔糜驎r,自動地銷毀動態(tài)分配的對象,防止內(nèi)存泄露。智能指針的核心實現(xiàn)技術(shù)是引用計數(shù),每使用它一次,內(nèi)部引用計數(shù)加1,每析構(gòu)一次內(nèi)部的引用計數(shù)減1,減為0時,刪除所指向的堆內(nèi)存。

C++11中提供了三種智能指針,使用這些智能指針時需要引用頭文件:

  • std::shared_ptr:共享的智能指針
  • std::unique_ptr:獨占的智能指針
  • std::weak_ptr:弱引用的智能指針,它不共享指針,不能操作資源,是用來監(jiān)視shared_ptr的。

1.shared_ptr的初始化

共享智能指針是指多個智能指針可以同時管理同一塊有效的內(nèi)存,共享智能指針shared_ptr 是一個模板類,如果要進行初始化有三種方式:通過構(gòu)造函數(shù)、std::make_shared輔助函數(shù)以及reset方法。共享智能指針對象初始化完畢之后就指向了要管理的那塊堆內(nèi)存,如果想要查看當(dāng)前有多少個智能指針同時管理著這塊內(nèi)存可以使用共享智能指針提供的一個成員函數(shù)use_count,函數(shù)原型如下:

// 管理當(dāng)前對象的 shared_ptr 實例數(shù)量,或若無被管理對象則為 0。
long use_count() const noexcept;

1.1 通過構(gòu)造函數(shù)初始化

// shared_ptr<T> 類模板中,提供了多種實用的構(gòu)造函數(shù), 語法格式如下:
std::shared_ptr<T> 智能指針名字(創(chuàng)建堆內(nèi)存);

測試代碼如下:

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    // 使用智能指針管理一塊 int 型的堆內(nèi)存
    shared_ptr<int> ptr1(new int(520));
    cout << "ptr1管理的內(nèi)存引用計數(shù): " << ptr1.use_count() << endl;
    // 使用智能指針管理一塊字符數(shù)組對應(yīng)的堆內(nèi)存
    shared_ptr<char> ptr2(new char[12]);
    cout << "ptr2管理的內(nèi)存引用計數(shù): " << ptr2.use_count() << endl;
    // 創(chuàng)建智能指針對象, 不管理任何內(nèi)存
    shared_ptr<int> ptr3;
    cout << "ptr3管理的內(nèi)存引用計數(shù): " << ptr3.use_count() << endl;
    // 創(chuàng)建智能指針對象, 初始化為空
    shared_ptr<int> ptr4(nullptr);
    cout << "ptr4管理的內(nèi)存引用計數(shù): " << ptr4.use_count() << endl;

    return 0;
}

測試代碼輸出的結(jié)果如下:

ptr1管理的內(nèi)存引用計數(shù): 1
ptr2管理的內(nèi)存引用計數(shù): 1
ptr3管理的內(nèi)存引用計數(shù): 0
ptr4管理的內(nèi)存引用計數(shù): 0

如果智能指針被初始化了一塊有效內(nèi)存,那么這塊內(nèi)存的引用計數(shù)+1,如果智能指針沒有被初始化或者被初始化為nullptr空指針,引用計數(shù)不會+1。另外,不要使用一個原始指針初始化多個shared_ptr。

int *p = new int;
shared_ptr<int> p1(p);
shared_ptr<int> p2(p);		// error, 編譯不會報錯, 運行會出錯

1.2 通過拷貝和移動構(gòu)造函數(shù)初始化

當(dāng)一個智能指針被初始化之后,就可以通過這個智能指針初始化其他新對象。在創(chuàng)建新對象的時候,對應(yīng)的拷貝構(gòu)造函數(shù)或者移動構(gòu)造函數(shù)就被自動調(diào)用了。

void test02() {
    // 使用智能指針管理一塊 int 型的堆內(nèi)存, 內(nèi)部引用計數(shù)為 1
    shared_ptr<int> ptr1(new int(520));
    cout << "ptr1管理的內(nèi)存引用計數(shù): " << ptr1.use_count() << endl;
    //調(diào)用拷貝構(gòu)造函數(shù)
    shared_ptr<int> ptr2(ptr1);
    cout << "ptr2管理的內(nèi)存引用計數(shù): " << ptr2.use_count() << endl;
    shared_ptr<int> ptr3 = ptr1;
    cout << "ptr3管理的內(nèi)存引用計數(shù): " << ptr3.use_count() << endl;
    //調(diào)用移動構(gòu)造函數(shù)
    shared_ptr<int> ptr4(std::move(ptr1));
    cout << "ptr4管理的內(nèi)存引用計數(shù): " << ptr4.use_count() << endl;
    std::shared_ptr<int> ptr5 = std::move(ptr2);
    cout << "ptr5管理的內(nèi)存引用計數(shù): " << ptr5.use_count() << endl;
}

測試程序輸入的結(jié)果:

ptr1管理的內(nèi)存引用計數(shù): 1
ptr2管理的內(nèi)存引用計數(shù): 2
ptr3管理的內(nèi)存引用計數(shù): 3
ptr4管理的內(nèi)存引用計數(shù): 3
ptr5管理的內(nèi)存引用計數(shù): 3

如果使用拷貝的方式初始化共享智能指針對象,這兩個對象會同時管理同一塊堆內(nèi)存,堆內(nèi)存對應(yīng)的引用計數(shù)也會增加;如果使用移動的方式初始智能指針對象,只是轉(zhuǎn)讓了內(nèi)存的所有權(quán),管理內(nèi)存的對象并不會增加,因此內(nèi)存的引用計數(shù)不會變化。

1.3 通過std::make_shared初始化

通過C++提供的std::make_shared() 就可以完成內(nèi)存對象的創(chuàng)建并將其初始化給智能指針,函數(shù)源碼如下:

template<class T, class... Args>
shared_ptr<T> make_shared(Args&&... args) {
    return std::allocate_shared<T>(std::allocator<T>(), std::forward<Args>(args)...);
}

這個函數(shù)模板使用了可變模板參數(shù)(variadic templates)和完美轉(zhuǎn)發(fā)(perfect forwarding)的特性。它接受一個類型 T 和任意數(shù)量的參數(shù)(Args),然后調(diào)用 std::allocate_shared 來完成共享指針的初始化和對象的構(gòu)造。

可變模板參數(shù)是指函數(shù)模板的參數(shù)數(shù)量是可變的。在std::make_shared的情況下,它允許我們以任意數(shù)量的參數(shù)初始化對象。

舉例來說,假設(shè)有一個類 MyClass:

class MyClass {
public:
    MyClass(int a, double b, const std::string& c) {
        // 構(gòu)造函數(shù)的實現(xiàn)
    }
};

可以使用 std::make_shared 來創(chuàng)建一個 shared_ptr 并調(diào)用 MyClass 的構(gòu)造函數(shù):

#include <memory>
#include <string>

int main() {
    auto mySharedPtr = std::make_shared<MyClass>(42, 3.14, "Hello");
    // 使用 mySharedPtr
    return 0;
}

在這個例子中,MyClass 有一個帶有三個參數(shù)的構(gòu)造函數(shù),而 std::make_shared 允許你提供這三個參數(shù),以便在分配內(nèi)存時調(diào)用構(gòu)造函數(shù)進行對象初始化??勺兡0鍏?shù)的使用使得 std::make_shared 能夠處理任意數(shù)量的構(gòu)造函數(shù)參數(shù)。

測試代碼如下:

#include <iostream>
#include <string>
#include <memory>
using namespace std;

class Test
{
public:
    Test()
    {
        cout << "construct Test..." << endl;
    }
    Test(int x)
    {
        cout << "construct Test, x = " << x << endl;
    }
    Test(string str)
    {
        cout << "construct Test, str = " << str << endl;
    }
    ~Test()
    {
        cout << "destruct Test ..." << endl;
    }
};

int main()
{
    // 使用智能指針管理一塊 int 型的堆內(nèi)存, 內(nèi)部引用計數(shù)為 1
    shared_ptr<int> ptr1 = make_shared<int>(520);
    cout << "ptr1管理的內(nèi)存引用計數(shù): " << ptr1.use_count() << endl;

    shared_ptr<Test> ptr2 = make_shared<Test>();
    cout << "ptr2管理的內(nèi)存引用計數(shù): " << ptr2.use_count() << endl;

    shared_ptr<Test> ptr3 = make_shared<Test>(520);
    cout << "ptr3管理的內(nèi)存引用計數(shù): " << ptr3.use_count() << endl;

    shared_ptr<Test> ptr4 = make_shared<Test>("我是要成為海賊王的男人!!!");
    cout << "ptr4管理的內(nèi)存引用計數(shù): " << ptr4.use_count() << endl;
    return 0;
}

測試代碼輸出:

ptr1管理的內(nèi)存引用計數(shù): 1
construct Test...
ptr2管理的內(nèi)存引用計數(shù): 1
construct Test, x = 520
ptr3管理的內(nèi)存引用計數(shù): 1
construct Test, str = 我是要成為海賊王的男人!!!
ptr4管理的內(nèi)存引用計數(shù): 1
destruct Test ...
destruct Test ...
destruct Test ...

使用std::make_shared()模板函數(shù)可以完成內(nèi)存地址的創(chuàng)建,并將最終得到的內(nèi)存地址傳遞給共享智能指針對象管理。如果申請的內(nèi)存是普通類型,通過函數(shù)的()可完成地址的初始化,如果要創(chuàng)建一個類對象,函數(shù)的()內(nèi)部需要指定構(gòu)造對象需要的參數(shù),也就是類構(gòu)造函數(shù)的參數(shù)。

1.4 通過reset方法初始化

共享智能指針類提供的std::shared_ptr::reset方法函數(shù)原型如下:

void reset() noexcept;

template< class Y >
void reset( Y* ptr );

template< class Y, class Deleter >
void reset( Y* ptr, Deleter d );

template< class Y, class Deleter, class Alloc >
void reset( Y* ptr, Deleter d, Alloc alloc );
  • ptr:指向要取得所有權(quán)的對象的指針
  • d:自定義的刪除器(deleter)
  • aloc:自定義的分配器

其中最常見的是不帶任何參數(shù)的形式和帶一個指針參數(shù)的形式。

不帶參數(shù)的reset

template<class Y>
void reset();

這個版本的 reset 將 shared_ptr 置為空,即它不再擁有任何對象。如果此時 shared_ptr 是最后一個擁有某個對象的智能指針,它會調(diào)用對象的析構(gòu)函數(shù)來釋放資源。

std::shared_ptr<int> ptr = std::make_shared<int>(42);
ptr.reset();  // ptr 現(xiàn)在為空,且釋放了之前的對象

帶指針參數(shù)的reset

template<class Y>
void reset(Y* ptr);

這個版本的 reset 會替換 shared_ptr 當(dāng)前所管理的對象,并且使用參數(shù) ptr 所指向的對象。如果此時 shared_ptr 是最后一個擁有某個對象的智能指針,它會調(diào)用原對象的析構(gòu)函數(shù)來釋放資源,然后開始管理新的對象。

std::shared_ptr<int> ptr = std::make_shared<int>(42);
ptr.reset(new int(10));  // ptr 不再指向之前的對象,而是指向一個新的 int 對象

帶指針和自定義刪除器的 reset

template<class Y, class Deleter>
void reset(Y* ptr, Deleter deleter);

這個版本的 reset 允許你指定一個自定義的刪除器(deleter),該刪除器將在 shared_ptr 管理的對象銷毀時被調(diào)用。這允許你使用 shared_ptr 管理通過非默認方式分配的資源。

std::shared_ptr<int> ptr(new int, [](int* p) { delete p; });
ptr.reset(new int, [](int* p) { delete[] p; });

帶指針、自定義刪除器和分配器的 reset

template<class Y, class Deleter, class Alloc>
void reset(Y* ptr, Deleter deleter, Alloc alloc);

這個版本的 reset 允許你指定自定義的刪除器和分配器。通常情況下,std::shared_ptr 使用默認分配器 std::allocator 和默認刪除器 delete。

std::shared_ptr<int> ptr(new int, [](int* p) { delete p; }, std::allocator<int>());

來看一下這個例子:

#include <iostream>
#include <string>
#include <memory>
using namespace std;

int main()
{
    // 使用智能指針管理一塊 int 型的堆內(nèi)存, 內(nèi)部引用計數(shù)為 1
    shared_ptr<int> ptr1 = make_shared<int>(520);
    shared_ptr<int> ptr2 = ptr1;
    shared_ptr<int> ptr3 = ptr1;
    shared_ptr<int> ptr4 = ptr1;
    cout << "ptr1管理的內(nèi)存引用計數(shù): " << ptr1.use_count() << endl; // 4
    cout << "ptr2管理的內(nèi)存引用計數(shù): " << ptr2.use_count() << endl; // 4
    cout << "ptr3管理的內(nèi)存引用計數(shù): " << ptr3.use_count() << endl; // 4
    cout << "ptr4管理的內(nèi)存引用計數(shù): " << ptr4.use_count() << endl; // 4

    ptr4.reset();
    cout << "ptr4管理的內(nèi)存引用計數(shù): " << ptr4.use_count() << endl; // 0
    ptr4.reset(new int(250));
    cout << "ptr1管理的內(nèi)存引用計數(shù): " << ptr1.use_count() << endl; // 3
    cout << "ptr2管理的內(nèi)存引用計數(shù): " << ptr2.use_count() << endl; // 3
    cout << "ptr3管理的內(nèi)存引用計數(shù): " << ptr3.use_count() << endl; // 3
    cout << "ptr4管理的內(nèi)存引用計數(shù): " << ptr4.use_count() << endl; // 1

    return 0;
}

對于一個未初始化的共享智能指針,可以通過reset方法來初始化,當(dāng)智能指針中有值的時候,調(diào)用reset會使引用計數(shù)減1。

1.5 獲取原始指針

通過智能指針可以管理一個普通變量或者對象的地址,此時原始地址就不可見了。當(dāng)我們想要修改變量或者對象中的值的時候,就需要從智能指針對象中先取出數(shù)據(jù)的原始內(nèi)存的地址再操作,解決方案是調(diào)用共享智能指針類提供的get()方法,其函數(shù)原型如下:

T* get() const noexcept;

測試代碼如下:

#include <iostream>
#include <string>
#include <memory>
using namespace std;

int main()
{
    int len = 128;
    shared_ptr<char> ptr(new char[len]);
    // 得到指針的原始地址
    char* add = ptr.get();
    fill(ptr.get(), ptr.get() + len, '\0');
    const char* source = "我愛cpp";
    copy(source, source + strlen(source) + 1, ptr.get());
    cout << "string: " << ptr.get() << endl;

    shared_ptr<int> p(new int);
    *p = 100;
    cout << *p.get() << "  " << *p << endl; // 100 100

    return 0;
}

2.指定刪除器

當(dāng)智能指針管理的內(nèi)存對應(yīng)的引用計數(shù)變?yōu)?的時候,這塊內(nèi)存就會被智能指針析構(gòu)掉了。另外,我們在初始化智能指針的時候也可以自己指定刪除動作,這個刪除操作對應(yīng)的函數(shù)被稱之為刪除器,這個刪除器函數(shù)本質(zhì)是一個回調(diào)函數(shù),我們只需要進行實現(xiàn),其調(diào)用是由智能指針完成的。

#include <iostream>
#include <memory>
using namespace std;

// 自定義刪除器函數(shù),釋放int型內(nèi)存
void deleteIntPtr(int* p)
{
    delete p;
    cout << "int 型內(nèi)存被釋放了...";
}

int main()
{
    shared_ptr<int> ptr(new int(250), deleteIntPtr);
    return 0;
}

刪除器函數(shù)也可以是lambda表達式,因此代碼也可以寫成下面這樣:

int main()
{
    shared_ptr<int> ptr(new int(250), [](int* p) {delete p; });
    return 0;
}

在上面的代碼中,lambda表達式的參數(shù)就是智能指針管理的內(nèi)存的地址,有了這個地址之后函數(shù)體內(nèi)部就可以完成刪除操作了。

管理動態(tài)數(shù)組

在C++11中使用shared_ptr管理動態(tài)數(shù)組時,需要指定刪除器,因為std::shared_ptr的默認刪除器不支持?jǐn)?shù)組對象,具體的處理代碼如下:

int main()
{
    shared_ptr<int> ptr(new int[10], [](int* p) {delete[]p; });
    return 0;
}

在刪除數(shù)組內(nèi)存時,除了自己編寫刪除器,也可以使用C++提供的std::default_delete()函數(shù)作為刪除器,這個函數(shù)內(nèi)部的刪除功能也是通過調(diào)用delete來實現(xiàn)的,要釋放什么類型的內(nèi)存就將模板類型T指定為什么類型即可。具體處理代碼如下:

int main()
{
    shared_ptr<int> ptr(new int[10], default_delete<int[]>());
    return 0;
}

另外,我們還可以自己封裝一個make_shared_array方法來讓shared_ptr支持?jǐn)?shù)組,代碼如下:

#include <iostream>
#include <memory>
using namespace std;

template <typename T>
shared_ptr<T> make_share_array(size_t size)
{
    // 返回匿名對象
    return shared_ptr<T>(new T[size], default_delete<T[]>());
}

int main()
{
    shared_ptr<int> ptr1 = make_share_array<int>(10);
    cout << ptr1.use_count() << endl;
    shared_ptr<char> ptr2 = make_share_array<char>(128);
    cout << ptr2.use_count() << endl;
    return 0;
}

資源的所有權(quán)轉(zhuǎn)移

另一方面: 指定刪除器也用于資源的所有權(quán)轉(zhuǎn)移

刪除器允許你將資源的所有權(quán)轉(zhuǎn)移給智能指針。這對于使用自定義釋放邏輯的資源管理類很有幫助,因為你可以將資源的釋放責(zé)任委托給智能指針。

class CustomResource {
public:
    CustomResource(int* data) : data_(data) {}
    void release() { delete[] data_; }

private:
    int* data_;
};

// 將自定義資源類的釋放函數(shù)作為刪除器
std::shared_ptr<CustomResource> customPtr(new CustomResource(new int[10]), [](CustomResource* resource) {
    resource->release();
    delete resource;
});

到此這篇關(guān)于C++共享智能指針shared_ptr的實現(xiàn)的文章就介紹到這了,更多相關(guān)C++ shared_ptr內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

最新評論