1. 導入:なぜnewを直接書いてはいけないのか
C++におけるメモリ管理は、伝統的にnewとdeleteのペアに依存してきました。しかし、手動でのメモリ解放はメモリリークや二重解放(double free)といった致命的なバグの温床となります。C++11で導入されたstd::unique_ptrは所有権を明確にすることでこの課題を解決しましたが、std::unique_ptrを初期化する際に「生のnew」を呼び出すことには、例外安全性の観点でまだリスクが残されていました。
C++14で導入されたstd::make_uniqueは、この「生のnew」を排除し、コードをより簡潔かつ例外安全にするための必須ツールです。本記事では、なぜ実務でstd::make_uniqueが推奨されるのかを解説します。
2. 基礎知識:例外安全性とmake系の役割
例外安全性とは、関数実行中に例外が発生してもメモリリーク等の不整合を起こさない性質を指します。
例えば、関数 f(std::unique_ptr
1. new T() が実行される
2. g() が実行される(ここで例外発生!)
3. std::unique_ptrのコンストラクタが呼ばれる
もし2で例外が発生すると、1で確保したメモリは解放される場所を失い、メモリリークが発生します。std::make_uniqueはこの「new」と「unique_ptrへの格納」を一つの関数内で行うため、このような分断を防ぎ、例外が発生しても確実にメモリが管理されるよう設計されています。
3. 実装と解決策
std::make_uniqueを使用する際は、std::unique_ptrを使用する場合と同様に
4. サンプルプログラム
以下は、std::make_uniqueを活用した実用的な実装例です。
include
include
include
class User {
public:
// コンストラクタ
User(std::string name, int age) : name_(name), age_(age) {
std::cout << "User構築: " << name_ << std::endl;
}
~User() { std::cout << "User破棄: " << name_ << std::endl; }
void print() const { std::cout << name_ << " (" << age_ << "歳)" << std::endl; }
private:
std::string name_;
int age_;
};
int main() {
// 従来のnewによる生成(推奨されない)
// std::unique_ptr
// std::make_uniqueによる生成(推奨)
// 型名を一度しか書かないため、リファクタリングにも強い
auto p2 = std::make_unique
p2->print();
// スコープを抜けると自動的にメモリが解放される
return 0;
}
5. 応用・注意点
現場で活用する際のポイントをいくつか挙げます。
・明示的な配列生成:
std::make_uniqueは配列にも対応しています。std::make_unique
・カスタムデリータ:
std::make_uniqueはカスタムデリータ(独自の破棄処理)を渡す機能がありません。複雑なリソース管理が必要な場合は、引き続き std::unique_ptr
・アクセス権:
std::make_uniqueを使用するには、対象クラスのコンストラクタがpublicである必要があります。privateコンストラクタを持つシングルトン等の場合は、std::unique_ptrを直接使用するか、factoryメソッドを併用してください。
結論として、特別な事情がない限り、unique_ptrの生成には必ずstd::make_uniqueを使用するようにしましょう。コードの安全性が高まるだけでなく、可読性も向上します。

コメント