スマートポインタ深掘り
unique_ptr と shared_ptr 使い分ける。
概要
Modern C++では、raw ポインタではなく スマートポインタを使用します。
std::unique_ptr:単独所有
std::shared_ptr:複数所有
std::unique_ptr: 単独所有
unique_ptr は、 メモリを単独で所有します。 所有権の移譲のみ可能。
#include <iostream>
#include <memory>
class Resource {
public:
void use() { std::cout << "使用中" << std::endl; }
~Resource() { std::cout << "破棄" << std::endl; }
};
int main() {
std::unique_ptr<Resource> ptr1 = std::make_unique<Resource>();
ptr1->use();
// 所有権を移譲
std::unique_ptr<Resource> ptr2 = std::move(ptr1);
ptr2->use();
// ptr1 はもう使えない
// ptr1->use(); // エラー! nullptr になっている
return 0;
}
使用中
使用中
破棄
std::unique_ptr の配列
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int[]> arr = std::make_unique<int[]>(5);
for (int i = 0; i < 5; i++) {
arr[i] = i * 10;
std::cout << arr[i] << " ";
}
std::cout << std::endl;
return 0;
}
0 10 20 30 40
unique_ptr<int[]>を使えば、
delete[]を忘れません。
std::shared_ptr: 複数所有
shared_ptr は、 メモリを複数で共有します。 参照カウント方式で自動破棄。
#include <iostream>
#include <memory>
class Resource {
public:
void use() { std::cout << "使用中" << std::endl; }
~Resource() { std::cout << "破棄" << std::endl; }
};
int main() {
std::shared_ptr<Resource> ptr1;
{
std::shared_ptr<Resource> ptr2 = std::make_shared<Resource>();
ptr1 = ptr2; // 参照カウント +1
ptr2->use();
std::cout << "参照カウント: " << ptr1.use_count() << std::endl;
} // ptr2 のスコープ離脱、参照カウント -1
ptr1->use();
std::cout << "参照カウント: " << ptr1.use_count() << std::endl;
return 0;
}
使用中
参照カウント: 2
使用中
参照カウント: 1
破棄
shared_ptr の参照カウント
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
std::cout << "カウント: " << ptr1.use_count() << std::endl;
{
std::shared_ptr<int> ptr2 = ptr1;
std::cout << "カウント: " << ptr1.use_count() << std::endl;
std::shared_ptr<int> ptr3 = ptr1;
std::cout << "カウント: " << ptr1.use_count() << std::endl;
}
std::cout << "カウント: " << ptr1.use_count() << std::endl;
std::cout << "値: " << *ptr1 << std::endl;
return 0;
}
カウント: 1
カウント: 2
カウント: 3
カウント: 1
値: 42
unique_ptr vs shared_ptr
| 項目 | unique_ptr | shared_ptr |
|---|---|---|
| 所有 | 単独 | 複数 |
| オーバーヘッド | 最小 | 参照カウント |
| コピー | 不可 | 可能 |
| 移譲 | std::move | 通常でOK |
デフォルト:unique_ptr を使う。 複数所有が必須の場合のみ shared_ptr。
実践例:リスト管理
#include <iostream>
#include <memory>
#include <vector>
struct Item {
std::string name;
explicit Item(const std::string& n) : name(n) {}
~Item() { std::cout << "削除: " << name << std::endl; }
};
int main() {
std::vector<std::unique_ptr<Item>> items;
items.push_back(std::make_unique<Item>("りんご"));
items.push_back(std::make_unique<Item>("みかん"));
items.push_back(std::make_unique<Item>("バナナ"));
for (auto& item : items) {
std::cout << item->name << std::endl;
}
return 0;
}
りんご
みかん
バナナ
削除: バナナ
削除: みかん
削除: りんご
ポイント
- raw ポインタを避ける
- デフォルト:unique_ptr(単独所有)
- 複数所有:shared_ptr(参照カウント)
- std::make_unique/make_sharedを推奨
まとめ
- スマートポインタで自動メモリ管理
- unique_ptr は効率的、速度重視
- shared_ptr は柔軟、複数所有対応