問題の歴史:
通常の(生の)ポインタは、C++において動的メモリを扱うための従来のメカニズムです。これは汎用的な抽象メカニズムですが、残念ながら、メモリリーク、二重解放、ダングリングポインタのエラーといった多くのバグに非常に影響を受けやすいです。そのため、C++11から標準ライブラリには、オブジェクトのライフサイクルを自動的に管理するスマートポインタのテンプレートクラス(std::unique_ptr、std::shared_ptr、std::weak_ptr)が含まれています。
問題:
通常のポインタを使用する際、メモリの割り当てと解放の責任はプログラマーにあります。メモリ解放のエラーは、メモリリークやデータの破損、プログラムのクラッシュを引き起こします。特に、例外処理やポインタを他の関数に渡す場合には、特に複雑なケースが見られます。
解決策:
スマートポインタは、割り当てられたメモリをカプセル化し、所有者がいなくなったときに自動的に解放します。これによりRAII(Resource Acquisition Is Initialization)が実現されます。std::unique_ptrは排他的な所有権を提供し、std::shared_ptrは共有される所有権を、std::weak_ptrは「循環参照」を避けるための制御されない所有権を提供します。
コードの例:
#include <memory> void foo() { std::unique_ptr<int> p = std::make_unique<int>(5); // 例外が発生してもメモリは解放される // ... }
主な特徴:
std::unique_ptrを使って、new[]で作成した配列を管理できますか?
いいえ、配列にはstd::unique_ptr<T[]>を使用してください。これにより、delete[]が呼び出されます。
std::unique_ptr<int[]> arr(new int[10]);
標準のスマートポインタはすべてのメモリリークを防げますか?
いいえ。例えば、std::shared_ptr間の循環参照はメモリリークを引き起こします。これらのサイクルを断ち切るためにstd::weak_ptrを使用します。
スマートポインタは同じオブジェクトを二度「削除」することができますか?
いいえ、所有権のロジックを破らない限り(生のポインタを使用しない限り)。生のポインタを手動でコピーすると、削除が二度行われる可能性があります。
生のポインタが使用され、メモリは手動で解放され、例外が発生した場合にメモリが解放されない。
利点:
欠点:
std::unique_ptrとstd::make_uniqueを使用してオブジェクトを作成し、それを関数に渡す。
利点:
欠点: