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;
}
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;
// 分析 迴圈引用 原因 圖
}
// 解決迴圈引用問題 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;
}
//--------------------------------------------------------------------
// 仿函式
// 原理: 過載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等字串。