std::unique_ptr です。auto_ptr
が deprecated(廃止予定)になりましたので、今後は shared_ptr,
unique_ptr
がスマートポインタの主流になっていくことでしょう。
auto_ptr の後継という意味では
unique_ptr の方がより近いかもしれません。
unique_ptr は下記2つの形式をサポートします。
まずは unique_ptr を使ってみます。shared_ptr とほとんど同じ感じで使用できますが、コピーはできません。moveを使って管理者の譲渡と明示的に行う必要があります。
最初にシンプルな unique_ptr 使用例を紹介します。
unique_ptr を直接使用する例を記載します。
int 型の生成は、以下のように使用します。
std::unique_ptrup(new int);
値の初期化を一緒に行いたい場合、以下のように使用します。下記例は 10 で初期化しています。
std::unique_ptrup(new int(10));
配列を確保する場合は、以下のように使用します。
std::unique_ptrarr_up(new int[5]);
配列の値を初期化と一緒に行いたい場合、以下のように使用します。下記例は {1,2,3,4,5} で初期化しています。
std::unique_ptrarr_up(new int[5]{1,2,3,4,5});
make_unique による例を記載します。
int 型の生成は、以下のように使用します。
auto up = std::make_unique<int>();
値の初期化を一緒に行いたい場合、以下のように使用します。下記例は 77 で初期化しています。
auto up = std::make_unique<int>(77);
配列を確保する場合、以下のように使用します。下記例では、配列数 5 です。
make_unique を使って初期化を一緒に行うことはできないようです。
auto arr_up = std::make_unique<int[]>(5); // 要素はデフォルト初期化(ゼロでない場合あり)
for (int i = 0; i < 5; ++i) {
arr_up[i] = (i + 1) * 10;
}
上記内容を含む c++ プログラムを以下で紹介します。
ソースコード:
["unique_ptr_01.cpp"]
#include <iostream> // for std::cout
#include <new> // for std::nothrow, placement new
#include <memory> // for std::make_unique
/**
* @brief std::unique_ptr, std::make_unique のシンプルなサンプル
*/
int main() {
std::cout << "-- uqique_ptr simple examples start --\n";
// 1) 単一 int の std::unique_ptr
{
std::unique_ptr<int> up(new int(10));
std::cout << "*up = " << *up << "\n";
// delete は自動的に行われる
}
// 2) int 配列の std::unique_ptr
{
std::unique_ptr<int[]> arr_up(new int[5]{1,2,3,4,5});
for (int i = 0; i < 5; ++i) {
std::cout << "arr_up[" << i << "] = " << arr_up[i] << "\n";
}
// delete[] は自動的に行われる
}
// 3) 単一 int 生成、std::make_unique 版
{
auto up = std::make_unique<int>(77);
std::cout << "unique_ptr owns = " << *up << "\n";
// delete は自動的に行われる
}
// 4) 配列 int 生成、std::make_unique 配列版(C++14 以降)
{
auto arr_up = std::make_unique<int[]>(5); // 要素はデフォルト初期化(ゼロでない場合あり)
for (int i = 0; i < 5; ++i) {
arr_up[i] = (i + 1) * 10;
}
std::cout << "make_unique<int[]> contents:";
for (int i = 0; i < 5; ++i) {
std::cout << ' ' << arr_up[i];
}
std::cout << '\n';
// delete[] は自動的に行われる
}
std::cout << "-- uqique_ptr simple examples end --\n";
return 0;
}
ビルド方法:
g++ -std=c++17 -Wall -Wextra -Wpedantic -O2 unique_ptr_01.cpp -o unique_ptr_01.out
実行結果:
$ ./unique_ptr_01.out -- uqique_ptr simple examples start -- *up = 10 arr_up[0] = 1 arr_up[1] = 2 arr_up[2] = 3 arr_up[3] = 4 arr_up[4] = 5 unique_ptr owns = 77 make_unique<int[]> contents: 10 20 30 40 50 -- uqique_ptr simple examples end -- $
前の節で unique_ptr の簡単な使用方法について記載しました。
でも「本当に解放処理を自動的にやってくれてるのか心配」ですよね。
本節では、unique_ptr で class インスタンスを生成して、delete 無しで自動的にデストラクタが実行されることを確認してみます。
| コンパイラ : | g++ (Ubuntu 13.3.0-6ubuntu2~24.04), | 13.3.0 |
| OS : | Ubuntu 24.04 (WSL) | |
[ソースコード: "unique_ptr.cpp"]
#include <iostream> // cout, endl
#include <string> // string
#include <memory> // unique_ptr
#include <cstdlib> // EXIT_SUCCESS
class Person {
private:
std::string name_;
std::string phone_;
public:
/**
* @brief デフォルトコンストラクタ
* 空の名前と電話番号で委譲コンストラクタを呼ぶ
*/
Person()
: Person("", "") // 委譲コンストラクタ
{
}
/**
* @brief コピーコンストラクタ
* @param rhs コピー元
*/
Person(const Person& rhs)
: Person(rhs.name_, rhs.phone_)
{
}
/**
* @brief 値を指定するコンストラクタ
* @param name 氏名
* @param phone 電話番号
*/
Person(const std::string& name, const std::string& phone)
: name_(name), phone_(phone)
{
std::cout << "Person::Person()" << std::endl;
}
/**
* @brief デストラクタ
*/
virtual ~Person()
{
std::cout << "Person::~Person()" << std::endl;
}
/**
* @brief 名前を取得する
* @return 名前への参照
*/
const std::string& getName() const
{
return name_;
}
/**
* @brief 電話番号を取得する
* @return 電話番号への参照
*/
const std::string& getPhone() const
{
return phone_;
}
};
// Person を出力する演算子オーバーロード
std::ostream& operator<<(std::ostream& os, const Person& p)
{
return (os << '(' << p.getName() << ',' << p.getPhone() << ')');
}
/**
* @brief Person をコンソール出力する(null 安全)
* @param person 出力対象の unique_ptr
*/
void coutPerson(const std::unique_ptr<Person>& person)
{
if (person) {
std::cout << *person << std::endl;
} else {
std::cout << "(null)" << std::endl;
}
}
/**
* @brief unique_ptr が指す Person を差し替える(所有権は引数で渡された参照側に残る)
* @param person 差し替え対象の unique_ptr への参照
*/
void replacePerson(std::unique_ptr<Person>& person)
{
person = std::make_unique<Person>("bar_2", "090-****-????");
}
int main()
{
// unique_ptr 生成
std::unique_ptr<Person> foo = std::make_unique<Person>("foo", "090-****-0123");
std::unique_ptr<Person> bar = std::make_unique<Person>("bar", "090-****-5555");
std::unique_ptr<Person> hoge;
// コンソール出力
coutPerson(foo); // (foo,090-****-0123)
coutPerson(bar); // (bar,090-****-5555)
coutPerson(hoge); // (null)
// Person 差し替え
replacePerson(bar);
coutPerson(bar); // (bar_2,090-****-????)
// 所有権の移動
hoge = std::move(foo);
// hoge = foo; // これはコンパイルエラーになる
coutPerson(foo); // (null)
coutPerson(hoge); // (foo,090-****-0123)
return EXIT_SUCCESS;
}
ビルド(例):
$ g++ -Wall -g make_unique.cpp -o make_unique.out
実行結果:
$ ./make_unique.out Person::Person() Person::Person() (foo,090-****-0123) (bar,090-****-5555) (null) Person::Person() Person::~Person() (bar_2,090-****-????) (null) (foo,090-****-0123) Person::~Person() Person::~Person() $
3箇所でクラス Person のデストラクタが実行されている("Person::~Person()" の部分)ことから、unique_ptr が期待通りに解放処理を行っていることがわかります。
std::unique_ptr を関数の引数に使用する例を紹介します。
関数の引数に unique_ptr を渡してみます。
関数に unique_ptr を渡したり、関数の中で unique_ptr の値を変更したり、といったことができます。
[ソースコード: "function_argument_sample.cpp"]
#include <iostream> // for std::cout
#include <memory> // for std::unique_ptr
#include <string> // for std::string
/**
* @brief 簡易的な Person クラス(サンプル用)
*/
class Person {
private:
std::string name_;
std::string phone_;
public:
Person(const std::string& name, const std::string& phone)
: name_(name), phone_(phone) {}
const std::string& getName() const { return name_; }
const std::string& getPhone() const { return phone_; }
};
/**
* @brief unique_ptr を読み取り専用で受け取り内容を表示する
* @param person コンテンツを参照する const 参照の unique_ptr
*/
void inspect(const std::unique_ptr<Person>& person)
{
if (person) {
std::cout << "inspect: " << person->getName() << ", " << person->getPhone() << "\n";
} else {
std::cout << "inspect: (null)\n";
}
}
/**
* @brief unique_ptr の参照を受け取り差し替える(所有権は呼び出し元に残る)
* @param person 差し替える対象の unique_ptr への参照
*/
void resetByRef(std::unique_ptr<Person>& person)
{
person = std::make_unique<Person>("replaced", "000-0000-0000");
}
/**
* @brief 値渡しで unique_ptr を受け取り所有権を取得する
* @param person 所有権を移動して受け取る unique_ptr
*/
void takeOwnership(std::unique_ptr<Person> person)
{
if (person) {
std::cout << "takeOwnership: received " << person->getName() << "\n";
} else {
std::cout << "takeOwnership: (null)\n";
}
// person はここでスコープを抜けると破棄される
}
/**
* @brief Person を生成して所有権を返すファクトリ関数
* @param name 氏名
* @param phone 電話番号
* @return 所有権を持つ unique_ptr<Person>
*/
std::unique_ptr<Person> makePerson(const std::string& name, const std::string& phone)
{
return std::make_unique<Person>(name, phone);
}
int main()
{
std::cout << "-- unique_ptr function argument sample start --\n";
// 1) makePerson で生成して戻り値で受け取る
auto p1 = makePerson("alice", "111-1111-1111");
inspect(p1);
// 2) resetByRef で参照を渡して差し替える
resetByRef(p1);
inspect(p1);
// 3) takeOwnership に値渡しで渡す(move を使う)
auto p2 = makePerson("bob", "222-2222-2222");
takeOwnership(std::move(p2));
inspect(p2); // p2 は null になっている
// 4) inspect に null を渡す
std::unique_ptr<Person> empty;
inspect(empty);
std::cout << "-- unique_ptr function argument sample end --\n";
return 0;
}
ビルド方法(g++):
g++ -std=c++23 -O2 -Wall -Wextra -Wpedantic function_argument_sample.cpp -o function_argument_sample.out
実行結果:
$ ./function_argument_sample.out -- unique_ptr function argument sample start -- inspect: alice, 111-1111-1111 inspect: replaced, 000-0000-0000 takeOwnership: received bob inspect: (null) inspect: (null) -- unique_ptr function argument sample end -- $
unique_ptr で動的配列を使用する例です。
T[]の特殊化が組み込まれており、[]演算子も実装されています。直観的で気持ちよく使えます。
ただし範囲外アクセスに対するチェック機構はありません。配列を使用したい場合、まずはコンテナの使用を検討するべきです。時々実行速度を問題にコンテナの使用を止めている場合がありますが、C++11で採用された
std::array を使用すれば処理速度の問題もありません。
#include <iostream> // cout, endl, EXIT_SUCCESS
#include <string> // string
#include <memory> // unique_ptr
using namespace std;
int main(int argc, char* argv[])
{
// std::unique_ptr の例
// default_delete の特殊化宣言は不要。delete[]が実行される。
// []演算子もある。
size_t n = 10;
std::unique_ptr<int[]> data(new int[n]);
cout << "std::unique_ptr<int> : ";
for (size_t i = 0; i < n; ++i){
data[i] = i;
}
for (size_t i = 0; i < n; ++i){
cout << data[i] << " ";
}
cout << endl;
return EXIT_SUCCESS;
}
c 言語で規定されている malloc, calloc でメモリを動的に取得した場合、解放に使用する関数は delete や delete[] ではなく free を使用します。デアロケータを指定することで unique_ptr のメモリ解放を free で行えるようにします。
#include <iostream> // cout, EXIT_SUCCESS
#include <memory> // shared_ptr, unique_ptr
#include <cstdlib> // malloc, free
using namespace std;
int main()
{
const size_t n = 256;
// unique_ptr<int[]>
std::unique_ptr<int[], decltype(&free)> memory(static_cast<int*> (malloc(n*sizeof(int))), std::free); // "decltype(&free)" は "void(*)(void*)" となるみたい
// std::unique_ptr<int[], void(*)(void*)> memory(static_cast<int*> (malloc(n*sizeof(int))), std::free); // だからこちらでもOK
if (memory.get()==nullptr){
// malloc失敗
}
else{
// malloc成功
for (size_t i = 0; i < n; ++i){
memory[i] = (int)i;
}
for (size_t i = 0; i < n; ++i){
cout << memory[i] << ", ";
}
cout << endl;
}
return EXIT_SUCCESS;
}
これは実装例をいくつか書いてみます。しっくりくるやつを使ってみてください。
シンプルにやるとこんな感じでしょうか。
#include <stdio.h> // fopen, fclose #include <stdlib.h> #include <memory> // unique_ptr #pragma warning(disable : 4996) // fopen, fclose の使用がエラーになるため void custom_fclose(FILE* fp){ if (fp != nullptr){ fclose(fp); } } int main(int argc, char* argv[]) { std::unique_ptr<FILE, decltype(&custom_fclose)> fp(fopen("fopen_test.txt", "w"), custom_fclose); // std::unique_ptr<FILE, void(*)(FILE*)> fp(fopen("fopen_test.txt", "w"), custom_fclose); if (fp.get() != nullptr){ // fopen成功 fprintf(fp.get(), "test\n"); } return EXIT_SUCCESS; // ここで custom_fclose が自動実行されます }
Lambda式 で記載するとこんな感じでしょうか。
#include <stdio.h> // FILE, fopen, fclose
#include <stdlib.h>
#include <memory> // unique_ptr
#pragma warning(disable : 4996)
int main(int argc, char* argv[])
{
std::unique_ptr<FILE, void(*)(FILE*)> fp(fopen("fopen_test.txt", "w"), [&](FILE* fp){
if (fp != nullptr){
fclose(fp);
}
});
// 一応ファイルオープンの成功/失敗を確認してからファイルを使用
if (fp.get() != nullptr){
// fopen成功
fprintf(fp.get(), "test\n");
}
return EXIT_SUCCESS;
// ここで custom_fclose が自動実行されます
}
ファクトリーパターン的に unique_ptr を返す関数を作成するならばこんな感じでしょうか。
#include <cstdio> // fopen, fclose
#include <iostream> // cout, EXIT_SUCCESS
#include <memory> // unique_ptr
using namespace std;
#pragma warning(disable : 4996)
std::unique_ptr<std::FILE, void(*)(FILE*)> make_file(const char * filename, const char * flags)
{
// fclose に NULL を渡して実行してはいけない。
// このため下記のような分岐処理が必要となる。
std::FILE * const fp = std::fopen(filename, flags);
return fp ? std::unique_ptr<std::FILE, void(*)(FILE*)>(fp, std::fclose) : std::unique_ptr<std::FILE, void(*)(FILE*)>();
}
int main()
{
auto fp = make_file("hello.txt", "wb");
// fp.get() をチェック
// fopen に失敗していたら fp.get() は NULL
if (fp.get()){
fprintf(fp.get(), "Hello world.");
}
}
私的には Lambda式 を使った2番目のやつが一番シンプルで好きかな。
c++14 から make_unique が加わりました。unique_ptr 直接使用せず make_unique によるコーディングが推奨されています。
[ソースコード: "make_shared_01.cpp"]
#include <iostream> // for std::cout
#include <new> // for std::nothrow, placement new
#include <memory> // for std::make_unique
/**
* @brief std::make_shared のシンプルなサンプル
*/
int main() {
std::cout << "-- make_unique simple examples start --\n";
// 1) 単一 int 生成、std::make_unique 版
{
auto up = std::make_unique<int>();
std::cout << "make_unique owns = " << *up << "\n";
// delete は自動的に行われる
}
// 2) 単一 int 生成、std::make_unique 版
{
auto up = std::make_unique<int>(77);
std::cout << "make_unique owns = " << *up << "\n";
// delete は自動的に行われる
}
std::cout << "-- make_unique simple examples end --\n";
return 0;
}
ビルド方法:
g++ -std=c++20 -g -Wall -Wextra -Wpedantic make_unique_01.cpp -o make_unique_01.out
実行結果:
$ ./make_shared_01.out -- shared_ptr simple examples start -- shared_ptr owns = 0 shared_ptr owns = 77 -- shared_ptr simple examples end -- $
class インスタンスの生成もやってみます。
[ソースコード: "make_unique_02.cpp"]
#include <iostream> // cout, endl
#include <string> // string
#include <memory> // unique_ptr
#include <cstdlib> // EXIT_SUCCESS
class Person {
private:
std::string name_;
std::string phone_;
public:
/**
* @brief デフォルトコンストラクタ
* 空の名前と電話番号で委譲コンストラクタを呼ぶ
*/
Person()
: Person("", "") // 委譲コンストラクタ
{
}
/**
* @brief コピーコンストラクタ
* @param rhs コピー元
*/
Person(const Person& rhs)
: Person(rhs.name_, rhs.phone_)
{
}
/**
* @brief 値を指定するコンストラクタ
* @param name 氏名
* @param phone 電話番号
*/
Person(std::string name, std::string phone)
: name_(std::move(name)), phone_(std::move(phone))
{
std::cout << "Person::Person()" << std::endl;
}
/**
* @brief デストラクタ
*/
virtual ~Person()
{
std::cout << "Person::~Person()" << std::endl;
}
/**
* @brief 名前を取得する
* @return 名前への参照
*/
const std::string& getName() const
{
return name_;
}
/**
* @brief 電話番号を取得する
* @return 電話番号への参照
*/
const std::string& getPhone() const
{
return phone_;
}
};
// Person を出力する演算子オーバーロード
std::ostream& operator<<(std::ostream& os, const Person& p)
{
return (os << '(' << p.getName() << ',' << p.getPhone() << ')');
}
/**
* @brief Person をコンソール出力する(null 安全)
* @param person 出力対象の unique_ptr
*/
void coutPerson(const std::unique_ptr<Person>& person)
{
if (person) {
std::cout << *person << std::endl;
} else {
std::cout << "(null)" << std::endl;
}
}
/**
* @brief unique_ptr が指す Person を差し替える(所有権は引数で渡された参照側に残る)
* @param person 差し替え対象の unique_ptr への参照
*/
void replacePerson(std::unique_ptr<Person>& person)
{
person = std::make_unique<Person>("bar_2", "090-****-????");
}
int main()
{
// unique_ptr 生成
std::unique_ptr<Person> foo = std::make_unique<Person>("foo", "090-****-0123");
std::unique_ptr<Person> bar = std::make_unique<Person>("bar", "090-****-5555");
std::unique_ptr<Person> hoge;
// コンソール出力
coutPerson(foo); // (foo,090-****-0123)
coutPerson(bar); // (bar,090-****-5555)
coutPerson(hoge); // (null)
// Person 差し替え
replacePerson(bar);
coutPerson(bar); // (bar_2,090-****-????)
// 所有権の移動
hoge = std::move(foo);
// hoge = foo; // これはコンパイルエラーになる
coutPerson(foo); // (null)
coutPerson(hoge); // (foo,090-****-0123)
return EXIT_SUCCESS;
}
ビルド(例):
$ g++ -Wall -Wextra -Wpedantic -g make_unique_02.cpp -o make_unique_02.out
実行結果:
$ ./make_unique_02.out Person::Person() Person::Person() (foo,090-****-0123) (bar,090-****-5555) (null) Person::Person() Person::~Person() (bar_2,090-****-????) (null) (foo,090-****-0123) Person::~Person() Person::~Person() $
c++14 で追加された make_unique は T[] および T[N] に対応しています。
unique_ptr
を使用する場合、いろいろな理由から make_unique を使用することが推奨みたいです。
1次元配列を扱う例を記載します。
[ソースコード: "make_unique_03.cpp"]
#include <iostream> // for std::cout
#include <new> // for std::nothrow, placement new
#include <memory> // for std::make_shared
/**
* @brief std::shared_ptr, std::make_shared のシンプルなサンプル
*/
int main() {
std::cout << "-- make_unique simple examples start --\n";
// 1) 配列 int 生成、std::make_shared 配列版(C++20 以降)
{
const size_t array_size = 5;
auto arr_up = std::make_unique<int[]>(array_size); // 要素はデフォルト初期化(ゼロでない場合あり)
for (size_t i = 0; i < array_size; ++i) {
arr_up[i] = (i + 1) * 10;
}
std::cout << "make_shared<int[]> contents: ";
for (size_t i = 0; i < array_size; ++i) {
std::cout << ' ' << arr_up[i];
}
std::cout << '\n';
// delete[] は自動的に行われる
}
std::cout << "-- make_unique simple examples end --\n";
return 0;
}
ビルド例:
$ g++ -Wall -Wextra -Wpedantic -g make_unique_03.cpp -o make_unique_03.out
実行結果:
$ ./make_unique_03.out -- make_unique simple examples start -- make_sharedcontents: 10 20 30 40 50 -- make_unique simple examples end --$
class インスタンスを make_unique で1次元配列にする例も作成してみましょう。
[ソースコード: "make_unique_04.cpp"]
#include <iostream> // for std::cout
#include <memory> // for std::make_unique
#include <string>
#include <cstdlib> // for EXIT_SUCCESS
// class Person
class Person {
private:
std::string name_;
std::string phone_;
public:
Person()
: Person("", "") // 委譲コンストラクタ
{
}
// コピーコンストラクタ
Person(const Person& rhs) // コピーコンストラクタ
: Person(rhs.name_, rhs.phone_) // 委譲コンストラクタ
{
}
Person(const std::string& name, const std::string& phone)
: name_(name), phone_(phone)
{
std::cout << "Person::Person(), name_ = " << name_ << std::endl;
}
// デストラクタ
virtual ~Person()
{
std::cout << "Person::~Person(), name_ = " << name_ << std::endl;
}
/**
* @brief コピー代入演算子
* @param rhs コピー元のPersonオブジェクト
* @return Person& このオブジェクトへの参照
*/
Person& operator=(const Person& rhs) {
name_ = rhs.name_;
phone_ = rhs.phone_;
std::cout << "copy operator=, name_ = " << name_ << std::endl;
return *this;
}
/**
* @brief ムーブ代入演算子
* @param rhs ムーブ元のPersonオブジェクト
* @return Person& このオブジェクトへの参照
*/
Person& operator=(Person&& rhs) noexcept {
if (this != &rhs) {
name_ = std::move(rhs.name_);
phone_ = std::move(rhs.phone_);
std::cout << "move operator=, name_ = " << name_ << std::endl;
}
return *this;
}
/**
* @brief 名前を参照で取得
* @return const std::string& 名前の参照
*/
const std::string& getName() const
{
return name_;
}
/**
* @brief 電話番号を参照で取得
* @return const std::string& 電話番号の参照
*/
const std::string& getPhone() const
{
return phone_;
}
};
// グローバルな演算子オーバーロード */
std::ostream& operator<<(std::ostream& os, const Person& rhs)
{
return (os << '(' << rhs.getName() << ',' << rhs.getPhone() << ')');
}
// Person をコンソール出力
void PrintPerson(const Person& person)
{
std::cout << person << std::endl;
}
int main()
{
const size_t size_n = 4;
std::unique_ptr<Person[]> persons = std::make_unique<Person[]>(size_n);
auto zoo = Person("Zoo", "080-****-9999");
persons[0] = zoo;
persons[1] = Person("foo", "090-****-0123");
persons[2] = Person("bar", "090-****-5555");
persons[3] = Person("baz", "090-****-6666");
for (size_t i = 0; i < size_n; ++i) {
PrintPerson(persons[i]);
}
return EXIT_SUCCESS;
}
ビルド例:
$ g++ -Wall -Wextra -Wpedantic -g make_unique_04.cpp -o make_unique_04.out
実行結果:
$ ./make_unique_04.out Person::Person(), name_ = Person::Person(), name_ = Person::Person(), name_ = Person::Person(), name_ = Person::Person(), name_ = Zoo copy operator=, name_ = Zoo Person::Person(), name_ = foo move operator=, name_ = foo Person::~Person(), name_ = Person::Person(), name_ = bar move operator=, name_ = bar Person::~Person(), name_ = Person::Person(), name_ = baz move operator=, name_ = baz Person::~Person(), name_ = (Zoo,080-****-9999) (foo,090-****-0123) (bar,090-****-5555) (baz,090-****-6666) Person::~Person(), name_ = Zoo Person::~Person(), name_ = baz Person::~Person(), name_ = bar Person::~Person(), name_ = foo Person::~Person(), name_ = Zoo $
無駄なコンストラクタ、デストラクタ、の発生が気になりますが、この動作で正しいです。
2次元配列を扱う例を記載します。
[ソースコード: "make_unique_05.cpp"]
/**
* @brief std::unique_ptr と std::make_unique を使った2次元配列のサンプル
*
* このサンプルでは、std::unique_ptr を使って動的に2次元配列を作成し、
* 各要素に値を代入して表示します。
*/
#include <iostream>
#include <memory>
int main() {
const size_t rows = 3;
const size_t cols = 4;
// 行ごとに unique_ptr<int[]> を持つ unique_ptr の配列を作成
auto matrix = std::make_unique<std::unique_ptr<int[]>[]>(rows);
for (size_t i = 0; i < rows; ++i) {
matrix[i] = std::make_unique<int[]>(cols);
}
// 値の代入と表示
for (size_t i = 0; i < rows; ++i) {
for (size_t j = 0; j < cols; ++j) {
matrix[i][j] = static_cast<int>(i * cols + j);
std::cout << matrix[i][j] << " ";
}
std::cout << "\n";
}
return 0;
}
ビルド例:
$ g++ -std=c++23 -O2 -Wall -Wextra -Wpedantic make_unique_05.cpp -o make_unique_05.out $
実行結果:
$ ./make_unique_05.out 0 1 2 3 4 5 6 7 8 9 10 11 $
3次元配列を扱う例を記載します。
[ソースコード: "make_unique_06.cpp"]
/**
* @brief std::unique_ptr と std::make_unique を使った3次元配列のサンプル
*
* このサンプルでは、std::unique_ptr を使って動的に3次元配列を作成し、
* 各要素に値を代入して表示します。
*/
#include <iostream>
#include <memory>
int main() {
const size_t x = 2;
const size_t y = 3;
const size_t z = 4;
// 3次元配列: unique_ptr<unique_ptr<unique_ptr<int[]>[]>[]>
auto array3D = std::make_unique<std::unique_ptr<std::unique_ptr<int[]>[]>[]>(x);
for (size_t i = 0; i < x; ++i) {
array3D[i] = std::make_unique<std::unique_ptr<int[]>[]>(y);
for (size_t j = 0; j < y; ++j) {
array3D[i][j] = std::make_unique<int[]>(z);
}
}
// 値の代入と表示
for (size_t i = 0; i < x; ++i) {
for (size_t j = 0; j < y; ++j) {
for (size_t k = 0; k < z; ++k) {
array3D[i][j][k] = static_cast<int>(i * 100 + j * 10 + k);
std::cout << array3D[i][j][k] << " ";
}
std::cout << "\n";
}
std::cout << "---\n";
}
return 0;
}
ビルド例:
$ g++ -std=c++23 -O2 -Wall -Wextra -Wpedantic make_unique_06.cpp -o make_unique_06.out $
実行例:
$ ./make_unique_06.out 0 1 2 3 10 11 12 13 20 21 22 23 --- 100 101 102 103 110 111 112 113 120 121 122 123 --- $
本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。
|
The MIT License (MIT) Copyright © 2014 Kinoshita Hidetoshi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 2025-11-29 | - | "2._関数の引数に使う" を追加 |
| 2025-11-22 | - | "7._make_unique_配列版" を追加 |
| 2025-11-10 | - | "1._unique_ptr_を使ってみる", "6._make_unique" を更新 |
| 2022-07-13 | - | ページデザイン更新 |
| 2019-11-02 | - | 「6. make_unique 配列版」を追加 |
| 2019-10-26 | - | 「5. make_unique」を追加 |
| 2014-03-09 | - | 新規作成 |