欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++之智能指針初步及棄用auto_ptr的原因分析

 更新時間:2023年03月23日 14:54:28   作者:Dutkig  
這篇文章主要介紹了C++之智能指針初步及棄用auto_ptr的原因分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

RAII

使用局部對象來管理資源的技術(shù)

RAII的原理

RAII的四個步驟

裸指針存在的問題

delete后的指針變量就變成了一個失效指針(也叫作懸空指針)。

對于下面的代碼:

void Destroy(Object *op)
{
	delete op;
	delete[] op;
}

Object *op = new Object(10);
Object *arop = new Object[10];

Destroy(op);
Destroy(arop);

因此:

智能指針

智能指針的引入

智能指針是比原始指針更加智能的類,解決懸空指針多次刪除被指向?qū)ο螅约百Y源泄漏問題,通常用來確保指針的壽命和其指向?qū)ο蟮膲勖恢隆?/p>

智能指針雖然很智能,很容易被誤用,智能也是有代價的。

四種智能指針

  • auto_ptr
  • unqiue_ptr(唯一性智能指針)
  • shared_ptr(共享性智能指針)
  • weak_ptr(管理弱引用)

其中后三個是C11支持,并且第一個已經(jīng)被C11棄用。

C98中的auto_ptr所做的事情,就是動態(tài)分配對象以及當對象不再需要時自動執(zhí)行清理。

下面我們首先來了解一下為什么要將auto_ptr移除的原因:

因為該類型的智能指針意義不明確,使用淺拷貝方式時,兩個對象擁有同一塊資源:我們模仿源碼的邏輯

了解一下:比如下面的代碼:

class Object
{
    int value;
public:
    Object(int x = 0):value(x){cout<<"Create Object:"<<this<<endl;}
    ~Object(){cout<<"Destroy Object:"<<this<<endl;}

    int  & Value(){return value;}
    const int& Value() const{return value;}
};

template<class _Ty>
class my_auto_ptr
{
private:
    bool _Owns;//所有權(quán)
    _Ty* _Ptr;
public:
    my_auto_ptr(_Ty* p = NULL):_Owns(p != NULL),_Ptr(p){}
    ~my_auto_ptr()
    {
        if(_Owns)
        {
            delete _Ptr;
        }
        _Owns = false;
        _Ptr = NULL;
    }
    _Ty* get() const 
    {
        return _Ptr;
    }
    _Ty* operator->()const
    {
        return get();
    }
    _Ty & operator*()
    {
        return *get();
    }
    void reset(_Ty* p = NULL) 
    {
       if(_Owns)
       {
           delete _Ptr;
       }
       _Ptr = p;
    }
    _Ty * release()const//編譯要通過,要么異變,要么強轉(zhuǎn)成普通指針
    {
        _Ty* tmp = NULL;
        if(_Owns)
        {
            ((my_auto_ptr*)this)->_Owns = false;
            tmp = _Ptr;
            ((my_auto_ptr*)this)->_Ptr = NULL;
        }
        return tmp;
    }
    my_auto_ptr(const my_auto_ptr & op):_Owns(op._Owns)
    {
        if(_Owns)
        {
            _Ptr = op._Ptr;
        }
    }
};

void fun()
{
    my_auto_ptr<Object> pobj(new Object(10));//pobj是my_auto_ptr類型
    cout<<pobj->Value()<<endl;
    cout<<(*pobj).Value()<<endl;//(*pobj)是Object的堆區(qū)對象。*(pobj._Ptr).Value()
}
int main()
{
    my_auto_ptr<Object> pobja(new Object(10));
    my_auto_ptr<Object> pobjb(pobja);
}

相關(guān)函數(shù)解釋:

此時程序必然會導致程序崩潰引發(fā)異常,主函數(shù)結(jié)束時對同一部分資源釋放了兩次,堆內(nèi)存被釋放兩次

那么我們可能會考慮,將資源轉(zhuǎn)移,即修改拷貝構(gòu)造如下:利用是釋放函數(shù)

my_auto_ptr(const my_auto_ptr & op):_Owns(op._Owns),_Ptr(op.release())
    {}

看似好像解決了上面的問題,實則存在隱患

繼續(xù)來看:下面的代碼存在什么問題呢?

void fun(my_auto_ptr<Object> apx)
{
    int x = apx->Value();
    cout<<x<<endl;
}

int main()
{
    my_auto_ptr<Object> pobja(new Object(10));
    
    fun(pobja);

    int a = pobja->Value();
    cout<<a<<endl;
}

上述代碼的執(zhí)行邏輯如下:

  • pobja有兩個域擁有權(quán)域和指針域,拿pobja初始化形參apx時,會調(diào)動拷貝構(gòu)造函數(shù)
  • apx將自己的擁有權(quán)域設(shè)為1,調(diào)動release函數(shù),銷毀了pobja對象的資源后,返回堆區(qū)對象的地址,apx接收后將自身的指針域指向原先pobja所指向的堆區(qū)對象
  • fun函數(shù)結(jié)束,apx局部對象就會被析構(gòu),此時再打印a,對象其實已經(jīng)不存在了并且自身早已失去了pobja的擁有權(quán)。

綜上,此時智能指針的拷貝構(gòu)造函數(shù)的兩種寫法:

 my_auto_ptr(const my_auto_ptr & op):_Owns(op._Owns)
    {
        if(_Owns)
        {
            _Ptr = op._Ptr;
        }
    }
   
 my_auto_ptr(const my_auto_ptr & op):_Owns(op._Owns),_Ptr(op.release())
    {}
  • 第一種存在的問題:Object的資源會被兩個釋放兩次
  • 第二種存在的問題:解決了第一種問題,但是不能解決類似于實參對象初始化形參時,實參之前自身的資源丟失的問題,找不著了,因為這種情況太過于隱蔽,容易出錯,所以auto_ptr作為函數(shù)參數(shù)傳遞時一定要避免的?;蛟S你想到加上引用解決上面的問題,但是仔細思考后發(fā)現(xiàn),我們并不知道函數(shù)對傳入的傳入的auto_ptr做了什么,如果當中的某些操作使其失去了對對象的所有權(quán),那么這還可能會導致致命的執(zhí)行期錯誤。獲取再加上const 才是個不錯的選擇。

因此,C11標準之前的auto_ptr這個智能指針不被廣泛使用的原因就是:在某些應用場景下,拷貝構(gòu)造函數(shù)的意義不明確,同理賦值語句也是這個道理,意義同樣不明確,因為C11標準之前并不存在移動賦值和移動構(gòu)造的概念,還有就是之前談到的一個對象和一組對象的問題,對于自定義類型而言,auto_ptr的析構(gòu)函數(shù)僅能夠析構(gòu)一個對象,不能夠處理一組對象的情況,這些都是尚未解決的問題。

于是在C11中棄用,C17標準中直接移除。 

歷史淵源:

在STL庫之前,有一個功能更加強大的boost庫,STL為了與其抗衡,應急制造了STL,但制作的不夠完善,由此因為STL未解決auto_ptr的問題,因此STl內(nèi)的容器vector和list都不想和auto_ptr建立聯(lián)系。

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 全排列算法的非遞歸實現(xiàn)與遞歸實現(xiàn)的方法(C++)

    全排列算法的非遞歸實現(xiàn)與遞歸實現(xiàn)的方法(C++)

    本篇文章是對全排列算法的非遞歸實現(xiàn)與遞歸實現(xiàn)的方法進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • C++ Invalidaterect()函數(shù)作用案例詳解

    C++ Invalidaterect()函數(shù)作用案例詳解

    這篇文章主要介紹了C++ Invalidaterect()函數(shù)作用案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • C語言實現(xiàn)注冊登錄系統(tǒng)

    C語言實現(xiàn)注冊登錄系統(tǒng)

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)注冊登錄系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • Qt在vs2019中使用及設(shè)置方法

    Qt在vs2019中使用及設(shè)置方法

    這篇文章主要介紹了Qt在vs2019中使用及設(shè)置方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08
  • 可能是全網(wǎng)最詳細的Qt連接MySQL數(shù)據(jù)庫教程

    可能是全網(wǎng)最詳細的Qt連接MySQL數(shù)據(jù)庫教程

    QT眾所周知是一個開源的,以C++為底層的可視化工具庫,下面這篇文章主要給大家介紹了關(guān)于最詳細的Qt連接MySQL數(shù)據(jù)庫教程的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-04-04
  • C/C++實現(xiàn)發(fā)送與接收HTTP/S請求的示例代碼

    C/C++實現(xiàn)發(fā)送與接收HTTP/S請求的示例代碼

    HTTP(Hypertext Transfer Protocol)是一種用于傳輸超文本的協(xié)議,它是一種無狀態(tài)的、應用層的協(xié)議,用于在計算機之間傳輸超文本文檔,通常在 Web 瀏覽器和 Web 服務器之間進行數(shù)據(jù)通信,本文給大家介紹了C/C++發(fā)送與接收HTTP/S請求,需要的朋友可以參考下
    2023-11-11
  • C++ OpenCV實現(xiàn)與添加椒鹽噪聲和高斯噪音

    C++ OpenCV實現(xiàn)與添加椒鹽噪聲和高斯噪音

    圖像噪聲是圖像在獲取或是傳輸過程中受到隨機信號干擾,妨礙人們對圖像理解及分析處理的信號,本文為大家整理了C++結(jié)合OpenCV為圖像添加椒鹽噪聲和高斯噪音的代碼,需要的可以收藏一下
    2023-09-09
  • C++發(fā)郵件簡單實例詳解

    C++發(fā)郵件簡單實例詳解

    這篇文章主要為大家詳細介紹了C++發(fā)郵件的簡單實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • C++使用ImGUI框架開發(fā)一個簡單程序

    C++使用ImGUI框架開發(fā)一個簡單程序

    ImGui?是一個用于C++的用戶界面庫,跨平臺、無依賴,支持OpenGL、DirectX等多種渲染API,下面就跟隨小編一起學習一下如何使用ImGUI框架開發(fā)一個簡單程序吧
    2023-08-08
  • C語言中輸入函數(shù)(scanf()、fgets()和gets())的區(qū)別詳解

    C語言中輸入函數(shù)(scanf()、fgets()和gets())的區(qū)別詳解

    這篇文章主要給大家介紹了關(guān)于C語言中三種輸入函數(shù)(scanf()、fgets()和gets())區(qū)別的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧。
    2017-11-11

最新評論