智能指針shared_ptr的用法


  為了解決C++內存泄漏的問題,C++11引入了智能指針(Smart Pointer)。

  智能指針的原理是,接受一個申請好的內存地址,構造一個保存在棧上的智能指針對象,當程序退出棧的作用域范圍后,由於棧上的變量自動被銷毀,智能指針內部保存的內存也就被釋放掉了(除非將智能指針保存起來)。

  C++11提供了三種智能指針:std::shared_ptr, std::unique_ptr, std::weak_ptr,使用時需添加頭文件<memory>。

  shared_ptr使用引用計數,每一個shared_ptr的拷貝都指向相同的內存。每使用他一次,內部的引用計數加1,每析構一次,內部的引用計數減1,減為0時,刪除所指向的堆內存。shared_ptr內部的引用計數是安全的,但是對象的讀取需要加鎖。

  1. shared_ptr的基本用法
  • 初始化

  可以通過構造函數、std::make_shared<T>輔助函數和reset方法來初始化shared_ptr:

#include "stdafx.h"
#include <iostream>
#include <future>
#include <thread>

using namespace std;
class Person
{
public:
    Person(int v) {
        value = v;
        std::cout << "Cons" <<value<< std::endl;
    }
    ~Person() {
        std::cout << "Des" <<value<< std::endl;
    }

    int value;

};

int main()
{
    std::shared_ptr<Person> p1(new Person(1));// Person(1)的引用計數為1

    std::shared_ptr<Person> p2 = std::make_shared<Person>(2);

    p1.reset(new Person(3));// 首先生成新對象,然后引用計數減1,引用計數為0,故析構Person(1)
                            // 最后將新對象的指針交給智能指針

    std::shared_ptr<Person> p3 = p1;//現在p1和p3同時指向Person(3),Person(3)的引用計數為2

    p1.reset();//Person(3)的引用計數為1
    p3.reset();//Person(3)的引用計數為0,析構Person(3)
    return 0;
}

   注意,不能將一個原始指針直接賦值給一個智能指針,如下所示,原因是一個是類,一個是指針。

    std::shared_ptr<int> p4 = new int(1);// error

  reset()包含兩個操作。當智能指針中有值的時候,調用reset()會使引用計數減1.當調用reset(new xxx())重新賦值時,智能指針首先是生成新對象,然后將就對象的引用計數減1(當然,如果發現引用計數為0時,則析構舊對象),然后將新對象的指針交給智能指針保管。

  • 獲取原始指針  
    std::shared_ptr<int> p4(new int(5));
    int *pInt = p4.get();
  • 指定刪除器

  智能指針可以指定刪除器,當智能指針的引用計數為0時,自動調用指定的刪除器來釋放內存。std::shared_ptr可以指定刪除器的一個原因是其默認刪除器不支持數組對象,這一點需要注意。

  2. 使用shared_ptr需要注意的問題

  但凡一些高級的用法,使用時都有不少陷阱。

  • 不要用一個原始指針初始化多個shared_ptr,原因在於,會造成二次銷毀,如下所示:
        int *p5 = new int;
        std::shared_ptr<int> p6(p5);
        std::shared_ptr<int> p7(p5);// logic error
  • 不要在函數實參中創建shared_ptr。因為C++的函數參數的計算順序在不同的編譯器下是不同的。正確的做法是先創建好,然后再傳入。
    function(shared_ptr<int>(new int), g());
  • 禁止通過shared_from_this()返回this指針,這樣做可能也會造成二次析構。
  • 避免循環引用。智能指針最大的一個陷阱是循環引用,循環引用會導致內存泄漏。解決方法是AStruct或BStruct改為weak_ptr。
struct AStruct;
struct BStruct;

struct AStruct {
    std::shared_ptr<BStruct> bPtr;
    ~AStruct() { cout << "AStruct is deleted!"<<endl; }
};

struct BStruct {
    std::shared_ptr<AStruct> APtr;
    ~BStruct() { cout << "BStruct is deleted!" << endl; }
};

void TestLoopReference()
{
    std::shared_ptr<AStruct> ap(new AStruct);
    std::shared_ptr<BStruct> bp(new BStruct);
    ap->bPtr = bp;
    bp->APtr = ap;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM