導入
C++でメモリ管理を安全に行うために欠かせない「スマートポインタ(std::shared_ptr)」。その生成には、効率と安全性の観点からstd::make_sharedの使用が推奨されています。しかし、クラスの設計で「コンストラクタをprivateにして、インスタンス化を制御したい」という場面はよくあります。実は、std::make_sharedは内部で直接コンストラクタを呼び出すため、privateなコンストラクタにはアクセスできず、そのままではエラーになってしまいます。この記事では、この課題を解決し、スマートなコードを書く方法を解説します。
基礎知識
まず、なぜstd::make_sharedが使えないのかを理解しましょう。std::make_sharedは、指定された型に対してnew演算子を内部で実行し、メモリ確保とオブジェクト構築を一度に行います。このとき、コンストラクタがprivateであると、外部から呼び出すことができないためコンパイルエラーとなります。
解決策として、C++では「アクセス権限を一時的に与える」か「生成専用の窓口を作る」というアプローチをとります。
実装/解決策
解決策は主に2つあります。
1. friend指定を使う:std::make_sharedをクラスの友人に指定することで、privateコンストラクタへのアクセスを許可します。しかし、これはstd::make_sharedが内部で何をしているかに依存するため、少し複雑です。
2. publicな静的ファクトリーメソッド(ラッパー)を用意する:こちらの方が一般的で安全です。クラス内に「インスタンスを生成してshared_ptrを返す」staticな関数を用意し、その中でnewを使って生成します。
サンプルプログラム
以下は、publicな静的ファクトリーメソッドを使ってスマートポインタを生成する例です。
include
include
class MyClass {
private:
// コンストラクタをprivateにして直接のインスタンス化を禁止
MyClass() { std::cout << "MyClassが生成されました。" << std::endl; }
public:
// 外部から生成するための窓口(静的ファクトリーメソッド)
static std::shared_ptr
// コンストラクタはprivateですが、クラス内からならnew可能です
return std::shared_ptr
}
void sayHello() { std::cout << "こんにちは!" << std::endl; } }; int main() { // MyClass obj; // これはコンパイルエラーになります // ファクトリーメソッド経由で安全に生成 auto ptr = MyClass::create(); ptr->sayHello();
return 0;
}
応用・注意点
注意点:make_sharedを使わないことのデメリット
今回の手法では、std::make_sharedではなく、new演算子を直接使ってstd::shared_ptrを構築しています。これにより、メモリ確保が2回発生するため、std::make_sharedに比べてわずかに効率が落ちます。しかし、プライベート・コンストラクタという設計上の制約を守るためには、このトレードオフを受け入れるのが一般的です。
さらなる上級テクニック
もしどうしてもstd::make_sharedの効率性を維持したい場合は、空の構造体や「Passkeyイディオム」と呼ばれる手法を用いて、publicなコンストラクタを定義しつつ、特定の権限を持つオブジェクトがないと生成できないようにする方法もあります。まずは上記のファクトリーメソッドを使いこなし、設計の意図を明確にすることから始めてみてください。

コメント