最終更新:2026-10-12
C++ スマートポインタを関数引数にする¶
C++那些事:スマートポインタはいつ関数引数にすべきか¶
スマートポインタを引数に使う関数もあれば、生ポインタを使う関数もあり、どちらを使うべきなのでしょうか?使い分けの原則は何でしょうか?
この節ではabseilのtipsを使って解説します。
次のコードには何の問題があるでしょうか?
bool CanYouPetTheDog(const std::shared_ptr<Dog>& dog,
absl::Duration min_delay) {
return dog->GetLastPetTime() + min_delay < absl::Now();
}
CanYouPetTheDog関数はその dog 引数の所有権に影響を与えませんが、そのシグネチャは std::shared_ptr に格納することを要求しています。これにより、特定の所有権モデルへの不要な依存が生み出されてしまい、関数自体はそのような依存を必要としません。この依存により、呼び出し側は std::unique_ptr を使ったり、オブジェクトをスタック上に構築したりするといった、他のモデルを使うことができなくなります。
ポインタ/参照を使う¶
所有権が影響を受けないときは**参照またはポインタ**を使います。参照/ポインタを使うことで、特定の所有権モデルへの依存をなくし、関数が Dog 型の任意のオブジェクトに対して動作できるようになります。
bool CanYouPetTheDog(const Dog& dog, absl::Duration min_delay) {
return dog.GetLastPetTime() + min_delay < absl::Now();
}
上のように定義すれば、呼び出し側の所有権モデルがどのようなものであっても、この関数を呼び出すことができます:
Dog stack_dog;
if (CanYouPetTheDog(stack_dog, delay)) { ... }
auto heap_dog = std::make_unique<Dog>();
if (CanYouPetTheDog(*heap_dog, delay)) { ... }
CustomPetPtr<Dog> custom_dog = CreateDog();
if (CanYouPetTheDog(*custom_dog, delay)) { ... }
関数が渡された値を変更する場合は、変更可能な参照または生ポインタを渡し、上述と同じ使い方をします。
スマートポインタを使う¶
関数が所有権を変更する場合はスマートポインタを使います。次のコードは、異なるスマートポインタ引数のためにいくつかのオーバーロードを提供しています。最初のオーバーロードは渡されたオブジェクトの所有権を引き取ることを前提とし、2番目のオーバーロードは渡されたオブジェクトに対して共有参照を1つ追加します。この2つの操作は、呼び出し側が Dog の所有権をどのように扱うかによって決まります。スタック上にある Dog の所有権は引き取ることができません。スタックから所有権を取り出すことはできないからです。
class Human {
public:
...
void Adopt(std::unique_ptr<Dog> dog) {
pets_.push_back(std::move(dog));
}
void Adopt(std::shared_ptr<Cat> cat) {
pets_.push_back(std::move(cat));
}
private:
std::vector<std::shared_ptr<Pet>> pets_;
...
};
まとめ¶
所有権が移転も変更もされないのであれば、スマートポインタを関数引数にするのは避けるべきです。
関連トピック¶
- C++ 純粋仮想関数と抽象クラス | C++那些事
- C++ assert アサーション | C++那些事
- C++ ビットフィールド | C++那些事
- C 言語による C++ 多態の実現 | C++那些事
- C++ const 定数と修飾 | C++那些事
- C++ const ファミリー | C++那些事
- C++ 間接参照演算子 * | C++那些事
- C++ 初期化と代入 | C++那些事
- C++ 複数戻り値の書き方 | C++那些事
- C++ mutable キーワード | C++那些事