C ++動的メモリとスマートポインタ



C Dynamic Memory Smart Pointers



各プログラムには、空き領域またはヒープと呼ばれるメモリプールがあり、動的に割り当てられたオブジェクト、つまり実行中のプロセス中に割り当てられたオブジェクトを格納するために使用されます。動的に割り当てられたオブジェクトが不要になった場合は、オブジェクトが占めるスペースを明示的に表示する必要があることを明確にする必要があります。そうしないと、オブジェクトが発生します。 メモリーリーク

C ++では、動的メモリ管理は 新着削除 実施する。



new:ダイナミックメモリ内のオブジェクトにスペースを割り当て、オブジェクトへのポインタを返します
削除:動的オブジェクトへのポインタを受け入れ、オブジェクトを破棄し、それに関連付けられているメモリを解放します

まず、動的メモリ割り当ての例を見てください



void example(std::string & str) { std::string * pstr = new std::string(str) //Dynamic allocation of a string object and use str to initialize ... if (something_error()) //If there is an exception { throw exception() } str = *pstr delete pstr return }

このプログラムで例外が発生すると、例外がスローされてプログラムが終了し、メモリリークが発生します。これは、pstrポインタが指すスペースが解放されないためです。
頭に浮かぶ解決策:削除を例外処理ブロックに追加できます。ただし、プログラムが非常に大きい場合は、手動でスペースを解放するために、適切な場所に削除を追加することを常に忘れてしまいます。

プログラマーは怠惰で、常にトラブルを救う解決策を考えます。動的メモリ空間の管理に関して、オブジェクトを動的に作成する場合、オブジェクトの存続を手動で管理する必要はなく、オブジェクトを自動的に管理し、オブジェクトが存在しなくなったときにオブジェクトを自動的に解放する方法がありますか?オブジェクトが占めるメモリ空間を使用しますか?

この便利な方法の説明は、 駆逐艦 オブジェクトにデストラクタがある場合、オブジェクトの有効期限が切れると、そのデストラクタはアフターケアのために占有していたスペースを解放します。ここでのpstrは、デストラクタのない通常のポインタですが、pstrがクラスオブジェクトへのポインタである場合は、問題を解決できます。ポインタオブジェクトの有効期限が切れたら、デストラクタにメソッドを追加して、デストラクタが指すスペースを解放できるようにすることができます。

それは要約することができます: 基本型ポインタをクラスオブジェクトポインタとしてカプセル化し(ここでのクラスはテンプレートクラスであり、さまざまな型に適用できます)、ポインタが指すメモリ空間を削除するためにデストラクタにdeleteステートメントを記述します。



上記は、C ++でのスマートポインターの設計アイデアです。

これは、C ++のスマートポインタの簡単な紹介です。
新しいSTL(標準テンプレートライブラリ)は、動的オブジェクトを管理するための2つのスマートポインタタイプを提供します。 Areshared_ptr、unique_ptr。初期のC ++ 98はauto_ptrを提供していましたが、現在は廃止されています。
新しい標準では、shared_ptrが指すオブジェクトへの弱参照であるweak_ptrのコンパニオンクラスも提供されますが、ここでは説明しません。
スマートポインタの使用には、メモリヘッダーファイルを含める必要があることに注意してください。

新しい標準の2つのスマートポインタ 主な違いは、基になるポインターを管理する方法です

  • shared_ptr
    '参照カウント'を使用して、複数のshared_ptrが同じオブジェクトを指すことを許可します。参照カウントが0の場合、オブジェクトが解放される、オブジェクトを指すポインターの数を示す方法。
  • unique_ptr
    'exclusive'が指すオブジェクト。所有権は次のとおりです。特定のオブジェクトの場合、ポイントするスマートポインターは1つだけであり、スマートポインターのデストラクタはオブジェクトを削除できます。割り当て操作で所有権を譲渡できます。


スマートポインタを使用する場合は、次の点に注意してください。 すべてのスマートポインタークラスのコンストラクターは明示的なキーワードを持っているため、組み込みポインターをスマートポインターに暗黙的に変換することはできません。スマートポインターを初期化するには、直接初期化フォームを使用する必要があります。

shared_ptr<int> p1 = new int(1024) //Error, there is an implicit conversion int * converted to shared_ptr shared_ptr<int> p2(new int(1024)) //Correct, using the direct initialization form


デフォルトでは、スマートポインターの初期化に使用される通常のポインターは、動的メモリーを指している必要があります。通常のポインタがスタックメモリまたはその他の非ヒープメモリを指している場合、スマートポインタの有効期限が切れると、それに関連付けられているオブジェクトが削除されて解放されます。この時点で非ヒープメモリに作用し、エラーが発生します。
ただし、これを行う必要がある場合は、スマートポインターを定義するときに使用される、deleteではなく独自の削除ツールを提供する必要があります。shared_ptrp(q、d)(pがスマートポインターを引き継ぎます。qが指すオブジェクトの所有権。 qはT *タイプに変換でき、pはdeleteの代わりにオブジェクトdを呼び出します)。


以下は、スマートポインタの使用例です。

#include #include #include #include using namespace std class example { public: example(const string & s) :str(s) { cout << 'constructing...' << endl } ~example() { cout << 'destructing...' << endl } void show() { cout << str << endl } private: string str } int main(int argc,char * argv[]) { //Use smart pointers like you use ordinary pointers shared_ptr pshared(new example('this is shared_ptr')) pshared->show() unique_ptr punique(new example('this is unique_ptr')) punique->show() }

shared_ptr

スマートポインタshared_ptrを使用する場合は、make_shared標準ライブラリ関数を使用して動的メモリを割り当て、autoを使用してオブジェクトを可能な限り定義し、make_sharedによって返されたオブジェクトを保存します。 autoはオブジェクトタイプを自動的に認識します。

通常のポインタを直接使用してスマートポインタを初期化すると、同じ原因になる可能性があります メモリの一部が2回解放されます

int main(int argc,char * argv[]) { example * p = new example() shared_ptr p1(p) shared_ptr p2(p) }

2つのスマートポインタは通常のポインタで初期化されるため、オブジェクトの参照カウントは1です。p1が失敗すると、それが指すメモリが解放され、p2は失敗してもメモリを解放し続けます。

したがって、この状況を回避するには、make_sharedを使用することをお勧めします。

int main(int argc,char * argv[]) { auto p1 = make_shared() //atuo p2(p1) shared_ptrp2(p1) //Use smart pointer to initialize smart pointer //shared_ptr p2 = p1 //shared_ptr also supports assignment initialization }

unique_ptr

前述のように、auto_ptrは初期のC ++で提供され、auto_ptrとunique_ptrはどちらも「所有権」システムを使用してオブジェクトを「排他的に」使用します。 auto_ptrが後で破棄される理由は、auto_ptrが潜在的なメモリクラッシュの問題を引き起こすためです。

auto_ptr<string> p1(new string('example')) auto_ptr<string> p2 p2 = p1

p2は、p1が指すオブジェクトの引き継ぎ力を引き継ぎます。オブジェクト空間は解放されません。
ただし、後続のプロセスでp1を引き続き使用すると、p1が指すデータが無効になるため、エラーが発生します。

unique_ptrを使用する場合:

unique_ptr<string> p1(new string('example')) uniq_ptr<string> p2 p2 = p1 //When writing this statement, the compiler will immediately report an error, thinking that the statement is illegal, so as to avoid the ambiguity of p1 p2 = move(p1)//You can use move to transfer the ownership of the object pointed to by p1 to p2, and p1 becomes a null pointer, and reassign p1 p1 = make_unique()//Same as make_shared() function, it is better to use make_unique to return unique_ptr object

unique_ptrがauto_ptrよりも優れていることがわかります。

shared_ptrとは異なり、uniqueは通常のコピーまたは割り当て操作をサポートしていません。たとえば、次の状況は許可されていません。

unique_ptrp1(new example('hello')) unique_ptrp2(p1) //error unique_ptrp3 = p1 //error

ただし、例外が1つあります。
unique_ptrが破棄されるunique_ptrであるか、一時的な右辺値であり、今後も使用し続けることができない場合は、別のunique_ptrに割り当てることができます。 。それ以外の場合、コンパイラはエラーを報告します。

... unique_ptr fun(const string & str) { return unique_ptr(new example(str)) } ... int main(int argc,char * argv[]) { ... unique_ptr p = fun('hello') ... }

関数funは、一時的なunique_ptrを返します。 main関数では、pが一時的なunique_ptrが指すオブジェクトの所有権を引き継ぎ、一時的なunique_ptrが破棄されます。今後、この一時的なunique_ptrを使用することは不可能であるため、この割り当てはありません。疑わしい。

スマートポインタクラスでは、unique_ptrがC言語の配列に適用できる唯一のポインタであることにも言及する価値があります。

auto p = make_unique<int[]>(10) p[5] = 7

メモリを自動的に管理する場合、次の欠点があります。
1.メモリを解放するのを忘れた
2.リリースされたオブジェクトを使用します
3.同じメモリが2回解放されます
4.削除後にポインタをリセットするのを忘れた

したがって、動的メモリの手動管理を使用する場合は、これらに注意してください。ただし、スマートポインタを使用することをお勧めします。