C++ -- スマート ポインターについての深い理解

PS: スマート ポインターの簡単なアプリケーションについては、こちらを参照してください  http://t.csdn.cn/qN7IK

1. スマート ポインターの概要

C++ には、スマート ポインターには次の 3 つのバージョンがあります。

auto_ptr unique_ptrshared_ptr 

スマート ポインターの 3 つのバージョンの中で、shared_ptr が最も完璧ですが、auto_ptr は基本的に役に立ちません。unique_ptr は多くのシナリオで使用されません。スマート ポインターの場合は、RAII を実装し、operator* とoperator-> をオーバーロードして、ポインターのように動作させる必要があります。

2. スマートポインタ解析

    2.1 auto_ptr バージョン

        Auto_ptr の単純な実装:

    template<class T>
	class Auto_ptr
	{
	public:
        //管理权转移 auto_ptr
		Auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

		Auto_ptr(Auto_ptr<T>& ptr)
			:_ptr(ptr._ptr)
		{
			ptr._ptr=nullptr;
		}

		T& operator=(const T& ptr)
		{
			if (this != &ptr)
			{
				if (_ptr)
				{
					cout << "Delete:" << _ptr << endl;
					delete _ptr;
				}

				_ptr = ptr._ptr;
				ptr._ptr = nullptr;
			}

			return *this;

		}
        //析构函数需要写重载,不同常见需要不同析构函数
		/*~Auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}*/
		T& operator*()
		{
			return *this;
		}

		T* operator->()
		{
			return this;
		}
	private:
		T* _ptr=nullptr;
	};

auto_ptr はコピー構築をサポートしているため、ポインタの所有者が一意であることを保証するために、ここで所有権が転送されますが、所有権の転送後、コピーされたものは空になるため、これが大きな欠点であり、auto_ptr は放棄されます。

2.2. unique_ptr の使用   

template<class T>
	class Unique_ptr
	{
	public:
		Unique_ptr(T* ptr)
			:_ptr(ptr)
		{}

		Unique_ptr(Auto_ptr<T>& ptr) = delete;
		
		T& operator=(const T& ptr) = delete;
		/*~Auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}*/
		T& operator*()
		{
			return *this;
		}

		T* operator->()
		{
			return this;
		}
	private:
		T* _ptr = nullptr;
	};

unique_ptr の基礎となる層は、コピー構築と代入オーバーロードを直接使用しないため、コピー構築とコピー オーバーロードを必要としないシナリオでのみ使用できます。そのため、unique_ptr の    欠点は、unique_ptr がポインターを共有しないことです。別の unique_ptr にコピーしたり、関数に値として渡したり、コピーを必要とする標準テンプレート ライブラリ (STL) アルゴリズムで使用したりすることはできません。unique_ptr のみを移動できます。これは、メモリ リソースの所有権が別の unique_ptr に転送され、元の unique_ptr はこのリソースを所有しなくなることを意味します。

2.3shared_ptrの使用

template<class T>
	class Shared_ptr
	{
	public:
		Shared_ptr(T* ptr=nullptr)
			:_ptr(ptr)
			,_Pcount(new int(1))
		{
			;
		}
		void Release()
		{
			if (--(*_Pcount) == 0)
			{
				cout << "Delete:" << _ptr << endl;
				delete _ptr;
				delete _Pcount;
			}
		}

		~Shared_ptr()
		{
			Release();
		}

		Shared_ptr(Smart_ptr<T>& ptr)
			:_ptr(ptr)
		{
			(*_Pcount)++;
			ptr = nullptr;
		}
		Shared_ptr& operator=(Smart_ptr& ptr)
		{
			if (_ptr == ptr._ptr)
				return *this;
			Release();
			--(*_Pcount);

			_ptr = ptr._ptr;
			_Pcount = ptr._Pcount;
			*_Pcount++;
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _Pcount;
	};

unique_ptr に基づいて、shared_ptr バージョンという新しいバージョンが追加されました。

基本原理:オブジェクトが参照された回数を記録するものであり、参照回数が0回、​​つまりオブジェクトを指す最後の共有ポインタが破壊された場合、共有ポインタのデストラクタはそのオブジェクトを指しているメモリ領域を解放する。

特徴:それが指すリソースは共有されます。つまり、複数のshared_ptrが同じリソースを指すことができ、これを実現するために参照カウントメカニズムが内部的に使用されます。

共有ポインタ メモリ: 各shared_ptr オブジェクトは内部的に 2 つのメモリ位置を指します。

  • オブジェクトへのポインタ。
  • 参照カウントされたデータを制御するためのポインター。

新しいshared_ptrオブジェクトがポインタに関連付けられている場合、そのコンストラクタ内で、このポインタに関連付けられている参照カウントを1つインクリメントします。

shared_ptr オブジェクトがスコープ外になると、そのデストラクター内で、関連付けられたポインターの参照カウントが 1 つ減ります。参照カウントが 0 になった場合は、このメモリに関連付けられている他のshared_ptr オブジェクトがないことを意味し、この場合、delete 関数を使用してメモリを削除します。

shared_ptr は通常のポインタと同じように使用され、shared_ptr オブジェクトで * と -> を使用でき、他のshared_ptr オブジェクトと同様に比較できます。

ただし、shared_ptr の場合はまだ欠点があり、循環参照の場合はバグが発生するため、現時点ではweak_ptr を使用する必要があります。

2.4weak_ptrの使用

weak_ptrは、 とともに使用できる弱参照スマート ポインタですshared_ptrweak_ptr管理対象オブジェクトの参照カウントは増加しないため、オブジェクトの有効期間には影響しません。管理対象オブジェクトへのポインターは、のメンバー関数weak_ptrを通じて取得できますlock()shared_ptr

3. 概要:

3.1 スマート ポインターの原理:

この問題を解決するスマート ポインタのアイデアは、通常のポインタをラップし、スマート ポインタ オブジェクトの有効期限が切れたら、そのデストラクタに通常のポインタのメモリを解放させます。

auto_ptr (C++98、C++11 のスキームは廃止されました): 所有権モードを採用します。特定のオブジェクトに対して、1 つのスマート ポインターのみがそれを所有できるため、そのオブジェクトを所有するスマート ポインターのデストラクターのみが所有できます。オブジェクトを削除します。次に、割り当ての所有権を譲渡します。

unique_ptr (auto_ptr の代替): また、所有権モードを採用して排他的所有権または厳密な所有権の概念を実現し、同時に 1 つのスマート ポインターのみがオブジェクトをポイントできるようにします。

shared_ptr: 参照カウントを使用して共有所有権の概念を実装します。複数のスマート ポインターが同じオブジェクトを指すことができ、最後の参照が破棄されると、オブジェクトとその関連リソースが解放されます。参照カウントを使用して、リソースが複数のポインターによって共有されていることを示します。たとえば、代入時にはカウントが 1 ずつ増加し、ポインタの有効期限が切れるとカウントが 1 ずつ減少します。delete は、最後のポインターの有効期限が切れた場合にのみ呼び出されます。

weak_ptr: このタイプのポインタは通常、単独では使用されず (実用的ではありません)、shared_ptr タイプのポインタと一緒にのみ使用できます。weak_ptr 型ポインタは、指定されたヒープ メモリ空間の参照カウントに影響を与えず、循環参照問題の解決に使用できます。

3.2 使用シナリオ

プログラム内でヒープ(空き記憶領域)からメモリをnewで確保し、不要になったらdeleteで解放する必要があり、解放し忘れるとメモリリークが発生します。C++ では、このプロセスの自動化に役立つスマート ポインターが導入されました。スマート ポインタは、ポインタのように動作するクラス オブジェクトです。プログラムで同じオブジェクトへの複数のポインターを使用する場合は、shared_ptr を選択する必要があります。プログラムで同じオブジェクトへの複数のポインターが必要ない場合は、unique_ptr を使用できます。new [] を使用してメモリを割り当てる場合は、unique_ptr を選択する必要があります。 ; 関数が新しいメモリ割り当てを使用し、そのメモリへのポインタを返す場合、その戻り値の型を unique_ptr として宣言することは適切な選択です。

おすすめ

転載: blog.csdn.net/weixin_66828150/article/details/132416110