1. 程式人生 > >C++ 幾種智慧指標的簡單實現

C++ 幾種智慧指標的簡單實現

#pragma once

// 智慧指標 

// 定義個類來封裝資源的分配和釋放,在構造 函式完成資源的分配和初始化,在解構函式完成資源的

// 清理,可以 保證資源的正確初始化和釋放。

// 這裡簡單實現 AutoPtr、 ScopedPtr、ScopedArray以及 SharedPtr

 //------------------------------SmartPtr.h-------------------

template<typename T>

class AutoPtr //缺點 定義多個指向同一空間的ptr 只有一個控制 其他的都失效了 

{

public:

    AutoPtr(T* ptr = NULL)

        :_ptr(ptr)

    {}

    AutoPtr(AutoPtr<T>& ap)// 這裡沒有用const 為了修改ap的成員

        :_ptr(ap._ptr)

    {

        ap._ptr = NULL;// 只能有一個智慧指標 指向空間 (控制權

    }

    AutoPtr<T>& operator= (AutoPtr<T>& ap)

    {

        if(_ptr != ap._ptr)//排除 1 自賦值 2(常常會忽略) 雖然不是自賦值 但兩個的_ptr 指向同一空間

        {

            //第3種情況 兩個不指向同一空間

            delete _ptr;

            _ptr = ap._ptr;

            ap._ptr = NULL;

            return *this;

        }

    }

    ~AutoPtr()

    {

        if(_ptr)

        {

            cout<<"delete _ptr"<<_ptr<<endl;

            delete _ptr;

        }

    }

    T& operator*()

    {

        return *_ptr;

    }

    // 從原理來說 要兩個-> 

    // operator->()返回的是T* 再->才能訪問對應成員

    // 這裡編譯器做了處理

    // 如 operator++() operator++(int)

    T* operator->()

    {

        return _ptr;

    }

protected:

    T* _ptr;

};

void testAutoPtr()

{

    struct A

{

    int _a;

};

    AutoPtr<int> ap1(new int(1));

    AutoPtr<int> ap2(ap1);

    AutoPtr<int> ap3(new int(2));

    ap3 = ap2;

    *ap3 = 10;//運算子過載T& operator*()

    A* p4 = new A;

    p4->_a = 10;

    AutoPtr<A> ap4(new A);//運算子過載 T* operator->()

    ap4->_a = 4;

}

//  AutoPtr 的老版本的寫法 比之前的多了一個bool 型別的_owner

// 缺點 : 在 

// AutoPtr<int> ap1(new int (1)); 

//if (1)

// {

//        AutoPtr<int> ap2(ap1);

// }

// 這種場景下 ap2出了作用域 析構 釋放空間 但ap1指標還指向那塊空間 很危險(野指標)

template<typename T>

class AutoPtr

{

public:

AutoPtr(T* ptr = NULL)

        :_ptr(ptr)

    {}

AutoPtr(AutoPtr<T>& ap)

{

_ptr = ap._ptr;

_owner = true;

ap._owner = false;

}

~AutoPtr()

{

if (_owner)

{

delete _ptr;

_owner = false;

}

}

AutoPtr<T>& operator=(AutoPtr<T>& ap)

    {

        if(_ptr != ap._ptr)//排除 1 自賦值 2(常常會忽略) 雖然不是自賦值 但兩個的_ptr 指向同一空間

        {

            //第3種情況 兩個不指向同一空間

if (_owner == true)

{

delete _ptr;

}

_ptr = ap._ptr;

ap._owner = false;

_owner = true;

            return *this;

        }

    }

    T& operator*()

    {

        return *_ptr;

    }

T* operator->()

    {

        return _ptr;

    }

protected:

T* _ptr;

bool _owner;

};

void testAutoPtr()

{

    struct A

{

int _a;

};

    AutoPtr<int> ap1(new int(1));

    AutoPtr<int> ap2(ap1);

    AutoPtr<int> ap3(new int(2));

    ap3 = ap2;

    *ap3 = 10;//運算子過載T& operator*()

    A* p4 = new A;

    p4->_a = 10;

    AutoPtr<A> ap4(new A);//運算子過載 T* operator->()

    ap4->_a = 4;

}

//-----------------------------------------------------

template<typename T>

class ScopedPtr //缺點 不能解決 兩個物件之間的拷貝 

{

public:

    ScopedPtr(T* ptr = NULL)

        :_ptr(ptr)

    {}

    ~ScopedPtr()

    {

        if (_ptr)

        {

            cout<<"delete:"<<_ptr<<endl;

            delete _ptr;

        }

    }

    T& operator*()

    {

        return *_ptr;

    }

    T* operator->()

    {

        return _ptr;

    }

    //與AutoPtr相比 簡單 粗暴 直接加protected:防止拷貝構造和賦值過載 同時也防止別人在類外實現

protected:

    ScopedPtr(ScopedPtr<T>& sp);

    ScopedPtr<T>& operator=(ScopedPtr<T>& sp);

protected:

    T* _ptr;

};

void TestScopedPtr()

{

    ScopedPtr<int> sp1(new int(1));

   // ScopedPtr<int> sp2(sp1);

}

//------------------------------------------

template<typename T>

class SharedPtr

{

public:

    SharedPtr(T* ptr = NULL)

        :_ptr(ptr)

        ,_pCount(new long(1))

    {}

    ~SharedPtr()

    {

        _Release();

    }

    SharedPtr(const SharedPtr<T>& sp)

        :_ptr(sp._ptr)

        ,_pCount(sp._pCount)

    {

        ++(*_pCount);

    }

/************************************

   SharedPtr<T>& operator=(const SharedPtr<T>& sp)

   {

       // 考慮多種情況

       // 1 sp1 = sp1

       // 2 sp1 = sp2; ==>sp1,sp2管理著同一塊記憶體

       // 3 sp1 = sp3 sp1與sp3 指向的空間不同

       if (_ptr != sp._ptr)//排除1、 2

       {

  _Release();

  _ptr = sp._ptr;

  _pCount = sp._pCount;

  (*_pCount)++;

       }

        return *this;

   }

   ***************************************/

SharedPtr<T>& operator=(SharedPtr<T> sp)//不用引用 不用const

   {

      //現代 寫法

  swap(_ptr, sp._ptr);

  swap(_pCount, sp._pCount);

        return *this;

   }

wKiom1b9FuCCs3CyAAHZbScBavs620.bmp

   T& operator*()

   {

return *_ptr;

   }

   T* operator->()

   {

return _ptr;

   }

   long UseCount()

   {

return *_pCount;

   }

   T* GetPtr()

   {

return _ptr;

   }

protected:

void _Release()

{

if(--(*_pCount) == 0)

{

delete _ptr;

delete _pCount;

}

}

protected:

    T* _ptr;

    long* _pCount;

};

void TestSharedPtr()

{

    SharedPtr<int> sp1(new int(1));

    SharedPtr<int> sp2(sp1);

    SharedPtr<int> sp3(sp1);

   // int a;

   // const int* p = &a;

   //// int const * p = &a;

   // // error: (*p) = 1; 要是可修改的左值

   // // p = &a;

   //// int * p2 = p; // error  不能從const到非const

   // int * const p3 = &a;

   ////error  p3 = &a;要是可修改的左值

   // *p3 = 5;

cout<<"sp1:"<<sp1.UseCount()<<endl;

cout<<"sp2:"<<sp2.UseCount()<<endl;

cout<<"sp3:"<<sp3.UseCount()<<endl<<endl;

sp1 = sp1;

sp1 = sp2;

cout<<"sp1:"<<sp1.UseCount()<<endl;

cout<<"sp2:"<<sp2.UseCount()<<endl;

cout<<"sp3:"<<sp3.UseCount()<<endl<<endl;

SharedPtr<int> sp4(new int(2));

sp1 = sp4;

cout<<"sp1:"<<sp1.UseCount()<<endl;

cout<<"sp2:"<<sp2.UseCount()<<endl;

cout<<"sp3:"<<sp3.UseCount()<<endl;

cout<<"sp4:"<<sp4.UseCount()<<endl;

}

//-----------------------------------------------------

template<typename T>

class ScopedArray

{

public:

ScopedArray(T* ptr = NULL)

:_ptr(ptr)

{}

~ScopedArray()

{

if (_ptr)

{

delete[] _ptr;

}

}

/*T& operator*()

{

return *_ptr;

}

T* operator->()

{

return _ptr;

}*/

//不需要過載上面兩個 用operator[]

T& operator[](size_t index)

{

return _ptr[index];

}

protected:

ScopedArray(const ScopedArray<T>& sp);

ScopedArray<T>& operator=(const ScopedArray<T>& sp);

protected:

T* _ptr;

};

void TestScopedArray()

{

struct V

{

int _v;

};

ScopedArray<V> sa(new V[10]);

sa[0]._v = 1;

sa[2]._v = 11;

}

//-----------------------------test.cpp--

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>

using namespace std;

#include "SmartPtr.h"

//void Test1()

//{

//    int* p = new int(1);

//

//    if (1)

//    {

//        delete p;

//

//        return;

//    }

//    delete p;

//}

//void DoSomething()

//{

//    if (1)

//    {

//        throw 1;

//    }

//}

//

//void Test2()

//{

//    int* p = new int(1);

//    try

//    {

//        DoSomething();

//    }

//    catch(...)

//    {

//        delete p;

//        throw;

//    }

//

//    delete p;

//}

//

//void Test1()

//{

//    int* p = new int(1);

//    AutoPtr<int> ap(p);

//    if (1)

//    {

//       // delete p;

//

//        return;

//    }

//   // delete p;

//}

//void DoSomething()

//{

//    if (1)

//    {

//        throw 1;

//    }

//}

//

//void Test2()

//{

//    int* p = new int(1);

//    AutoPtr<int> ap(p);

//

//    DoSomething();

//    /*try

//    {

//        DoSomething();

//    }

//    catch(...)

//    {

//        delete p;

//        throw;

//    }

//

//    delete p;*/

//}

//

//int main()

//{

//    try

//    {

//        Test1();

//        Test2();

//    }

//    catch(...)

//    {

//        cout<<"未知異常"<<endl;

//    }

//

//    getchar();

//    return 0;

//}

int main()

{

    //TestSharedPtr();

TestScopedArray();

getchar();

    return 0;

}

//==================================

//==================================

//==================================

//

//1、 增加AutoPtr的另一種寫法(老版本寫法)

//2、 模擬SharedPtr的定置刪除器

//3、 定置刪除器和迴圈引用的場景並理解

// 智慧指標 使用庫中的 

#include <memory> // 這個標頭檔案包含 auto_ptr 、unique_ptr 、shared_ptr

using namespace std;

void test_auto_ptr()

{

//auto_ptr 用到 memory標頭檔案

auto_ptr<int> ap1(new int(1));

auto_ptr<int> ap2(ap1);

}

// scoped_ptr 在 C++ 11 中叫 unique_ptr

void test_scoped_ptr()

{

unique_ptr<int> sp1(new int(1));

// error unique_ptr<int> sp2(sp1);

}

void test_shared_ptr1()

{

shared_ptr<int> sp1(new int (1));

cout<<"sp1:"<<sp1.use_count()<<endl;

shared_ptr<int> sp2(sp1);

cout<<"sp1:"<<sp1.use_count()<<endl;

cout<<"sp2:"<<sp2.use_count()<<endl;

}

//--------------------------------------------------

void test_shared_ptr2()

{

struct Node

{

//Node* _next;

//Node* _prev;

shared_ptr<Node> _next; // 容易引起迴圈引用 解決方法詳見 下面weak_ptr

shared_ptr<Node> _prev;

~Node()

{

cout<<"delete:"<<this<<endl;

}

};

//  迴圈引用問題 這幾步都用的是 智慧指標 cur next _next _prev

shared_ptr<Node> cur(new Node());

shared_ptr<Node> next(new Node());

// 沒有下面兩句就可以釋放的 下面兩句引起迴圈引用

// *********重點***********

//cur->_next = next;

//next->_prev = cur;

// 分析 迴圈引用 原因 圖

wKioL1cFIJWCmTltAACqczONHcA770.png

}

// 解決迴圈引用問題 weak_ptr

void test_shared_ptr3()

{

struct Node

{

//Node* _next;

//Node* _prev;

/*shared_ptr<Node> _next;

shared_ptr<Node> _prev;*/

// 解決迴圈引用 (運用弱指標weak_ptr 不增加shared_ptr的count 增加自己weak_ptr 的引用計數 count)

//weak_ptr 過載了operator->() operator*()

// weak_ptr 唯一的目的 就是解決shared_ptr的死穴 迴圈引用問題 

// wead_ptr不能單獨使用 例如沒有weak_ptr<int> (int*)型別的建構函式 只有weak_ptr<shared_ptr> (shared_ptr&)

// 用弱指標 場景 內部含有指向對方的 智慧指標

weak_ptr<Node> _next;

weak_ptr<Node> _prev;

~Node()

{

cout<<"delete:"<<this<<endl;

}

};

//  迴圈引用問題 這幾步都用的是 智慧指標 cur next _next _prev

shared_ptr<Node> cur(new Node());

shared_ptr<Node> next(new Node());

cout<<"cur"<<cur.use_count()<<endl;

cout<<"next:"<<next.use_count()<<endl;

// *********重點**** weak_ptr解決迴圈引用問題 *******

cur->_next = next;

next->_prev = cur;

cout<<"cur"<<cur.use_count()<<endl;

cout<<"next:"<<next.use_count()<<endl;

}

wKioL1cFIWXBTDMaAAERVnah6ng378.png

//--------------------------------------------------------------------

// 仿函式

// 原理: 過載operator() ()

// 使用: 用Less建立一個結構體物件less

// 使用less() 看起來像函式呼叫 其實是使用了物件的operator()操作 這樣傳參傳如一個less物件就能用它的對應的operator()方法 這樣就能定製刪除器了

template<typename T>

struct Less 

{

bool operator() (const T& L, const T& R)

{

return L < R;

}

};

void test_less()

{

Less<int> less;

cout<<less(1, 2)<<endl;

}

//------------------------------

//模擬SharedPtr的定置刪除器

void test_shared_ptr4()

{

//場景1 沒問題

int* p1 = (int*)malloc(sizeof(int) * 10);

shared_ptr<int> sp1(p1);

// 場景2  析構出錯 不能將FILE* 轉換為int*

//FILE* p2 = fopen("test.txt", "r");

//shared_ptr<int> sp2(p2);

}

// 定製刪除器

struct Free

{

void operator()(void* ptr)

{

cout<<"Free:"<<ptr<<endl;

free(ptr);

}

};

struct FileClose //場景2的 刪除器

{

void operator()(void* fp)

{

cout<<"FileClose:"<<fp<<endl;

if (fp != NULL)

{

fclose((FILE*)fp);

}

}

};

void test_Free()

{

//場景1 沒問題

int* p1 = (int*)malloc(sizeof(int) * 10);

// ***********  重點 定製刪除器的應用

// Free() 建立一個匿名物件

shared_ptr<int> sp1(p1, Free()); // 重點 【給庫中的 shared_ptr 傳入 Free()的匿名物件】 下邊改進的自己的SharedPtr也用到這方法

// 場景2  析構出錯 不能將FILE* 轉換為int*

// 定製刪除器後不出錯

FILE* p2 = fopen("test.txt", "r");

shared_ptr<FILE> sp2(p2, FileClose());

}

//---------------------------------

//用定製刪除器 改進 之前的SharedPtr

template<typename T, typename D = DefaultDel<T>>//D 是刪除器結構體型別

class SharedPtr

{

public:

    SharedPtr(T* ptr = NULL)

        :_ptr(ptr)

        ,_pCount(new long(1))

    {}

SharedPtr(T* ptr, D del)

:_ptr(ptr)

,_pCount(new long(1))

,_del(del)

{}

    ~SharedPtr()

    {

        _Release();

    }

    SharedPtr(const SharedPtr<T, D>& sp)

        :_ptr(sp._ptr)

        ,_pCount(sp._pCount)

    {

        ++(*_pCount);

    }

SharedPtr<T, D>& operator=(SharedPtr<T,D> sp)//不用引用 不用const

   {

      //現代 寫法

  swap(_ptr, sp._ptr);

  swap(_pCount, sp._pCount);

        return *this;

   }

   T& operator*()

   {

return *_ptr;

   }

   T* operator->()

   {

return _ptr;

   }

   long UseCount()

   {

return *_pCount;

   }

   T* GetPtr()

   {

return _ptr;

   }

protected:

void _Release()

{

if(--(*_pCount) == 0)

{

//delete _ptr;

//********更換成——del()************

_del(_ptr);

delete _pCount;

}

}

protected:

    T* _ptr;

    long* _pCount;

D _del;

};

// 刪除器

template<typename T>

struct DefaultDel

{

void operator()(T* ptr)

{

delete ptr;

}

};

template<typename T>

struct FreeShared

{

void operator()(T* ptr)

{

free(ptr);

}

};

void TestDeleter()

{

SharedPtr<int, DefaultDel<int>> sp1(new int(10));

SharedPtr<int, FreeShared<int>> sp2((int*)malloc(sizeof(int) * 2));

SharedPtr<int> sp3(new int(1));

}

//-----------------------------------------------------

#include "SmartPtr.h"

int main()

{

//test_auto_ptr();

//test_shared_ptr4();

//test_less();

test_Free();

//TestDeleter();

getchar();

return 0;

}

相關推薦

C++ 智慧指標簡單實現

#pragma once// 智慧指標 // 定義個類來封裝資源的分配和釋放,在構造 函式完成資源的分配和初始化,在解構函式完成資源的// 清理,可以 保證資源的正確初始化和釋放。// 這裡簡單實現 AutoPtr、 ScopedPtr、ScopedArray以及 Share

C++智慧指標簡單實現

#include <iostream> #include <string> #include <vector> #include <list> namespace smart_pointer { // RAII(Resou

智慧指標的比較(std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::weak_ptr)

一、std::auto_ptr auto_ptr的建構函式接受原始指標作為引數,雖然它是一個物件,但是過載了operator*和operator->,可以把它用在大多數普通指標可用的地方。當退出作用域時,auto_ptr物件的解構函式會釋放原始指標。 例: int m

C++11】4智慧指標

|auto_ptr(不要使用的指標) 沒有智慧指標的c++時代,對堆記憶體的管理就是簡單的new delete。 但是缺點是容易忘了delete釋放,即使是資深碼農,也可能會在某一個地方忘記delete它,造成記憶體洩漏。 在實際工程中,我們往往更希望把精力放在應用層上,而不是費盡心思在語言的細

C++智慧指標簡單剖析

導讀 《C++ Primer Plus》第六版,其中關於智慧指標的章節解析的非常清晰。C++面試過程中,很多面試官都喜歡問智慧指標相關的 問題,比如你知道哪些智慧指標?shared_ptr的設計原理是什麼?如果讓你自己設計一個智慧指標,你如何完成?等等……。而且在看開源的C

java中實現單例模式的方式,簡單易懂

一、餓漢式單例類 public class Singleton { private Singleton(){ } private static Singleton instance = new Singleton();

C++過載——智慧指標實現

本文參照於狄泰軟體學院,唐佐林老師的——《C++深度剖析教程》 智慧指標背後的設計思想 在專案開發中,有一個臭名昭著的BUG——記憶體洩漏。不管是新手還是老手都容易犯這個錯誤。並且這種BUG很難查詢。 1. 編譯器並不能發現記憶體洩漏的問題。 2

JAVA多執行緒實現方式及簡單示例

 JAVA多執行緒的實現方式是JAVA基礎的一個重點,看過好多次,但不經常用就容易忘記,今天做一個總結,算是把JAVA基礎再夯實一下。 Java多執行緒實現方式主要有四種:繼承Thread類、實現Runnable介面、實現Callable介面通過FutureTask包裝器來

c++智慧指標實現

智慧指標(smart pointer)是儲存指向動態分配(堆)物件指標的類,用於生存期控制,能夠確保自動正確的銷燬動態分配的物件,防止懸垂指標的出現。在類物件中,如果定 義一個int型的指標,當多次使用同一個已存在的物件初始化一些新建物件時,已存在的物件和新建的物件都指向同

C++的4智慧指標剖析使用

1. 智慧指標背後的設計思想 我們先來看一個簡單的例子: void remodel(std::string & str) { std::string * ps = new std::string(str); ... if (weird_

C++利用智慧指標shared_ptr實現物件池

         C++中用new來分配物件,還要顯式的呼叫delete來析構物件,很容易造成記憶體洩露。所以在研究我們遊戲伺服器的程式碼的時候發現,我們將new函式封裝,在物件池上,利用shared_ptr的特性來自動釋放物件,同時實現了一種簡單的GC回收物件的機制。看完

C++學習筆記】詳解C++中的三智慧指標

一、簡介 由於 C++ 語言沒有垃圾回收機制,程式設計師每次 new出來的記憶體都要手動 delete。程式設計師忘記 delete,有可能就會造成記憶體洩漏,程式崩潰等嚴重的後果。用智慧指標便可以有效緩解這類問題,本文主要講解常見的智慧指標的用法。包括:s

簡單智慧指標實現

直上程式碼,大佬忽略 #include <iostream> #include <string> using namespace std; //定義智慧指標模板類 template<typename T> class SmartP

一個簡單智慧指標實現

#include<iostream> #include<memory> using namespace std; template<typename T> cla

C++中的三智慧指標分析(RAII思想)

智慧指標 首先我們在理解智慧指標之前我們先了解一下什麼是RAII思想。RAII(Resource Acquisition Is I

C程序編譯系統時簡單實現信息儲存

printf 程序 int fwrite 簡單 pan 文件 fclose 讀取 1 /*將學生信息從文件讀出*/ 2 void IO_ReadInfo() 3 { 4 FILE *fp; 5 int i; 6 7 if((fp=f

redis加鎖的實現

spa continue redis con 創建 -c print 發現 time 1. redis加鎖分類 redis能用的的加鎖命令分表是INCR、SETNX、SET 2. 第一種鎖命令INCR 這種加鎖的思路是, key 不存在,那麽 key 的值會先被初始化為

nginx虛擬主機三模式的簡單實現

_for nod send nop request user 模式 -s hit main配置段: user nginx; #指定用於運行worker進程的用戶和組 worker_processes 4; #worker的進程數;通常應該為CPU的核心數或核心數減1

SpringBoot定時任務的實現方式

配置文件 ride 可選 cron 文件 ref 而且 基於 cut 原文地址:SpringBoot幾種定時任務的實現方式 定時任務實現的幾種方式: Timer:這是java自帶的java.util.Timer類,這個類允許你調度一個java.util.TimerTask

c#: WinForm介面多語言簡單實現

終於有空整理下多語言實現思路。搜尋已有方案,有用不同resx檔案的,有每個控制元件動態設定的,有用反射去整的,頗為繁瑣。 結合專案中實現方法,並做簡化,實現通用的多語言切換方案,以做備忘。 它支援語言自定義新增與擴充,靈活易用,更易於維護。它以xml格式儲存語言資訊,支援自定義語言、ToolTip等字串。