SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Modern C++入門

メモリ利用を効率化! Modern C++のキモ「ムーブセマンティクス」

第4回 コピーと移動は明確に使い分けよう

  • X ポスト
  • このエントリーをはてなブックマークに追加

スマートポインタとムーブセマンティクス

 ここまでコピー、ムーブと扱ってきましたが、第3回で紹介したスマートポインタは、このムーブを実装したクラスです。ここからは、スマートポインタをムーブセマンティクスという観点でおさらいしてみます。

所有権のコピーを認めないスマートポインタ[C++14]

 最初は、所有権のコピーを認めないスマートポインタunique_ptrです。所有権、覚えていますか?値の保持や変更を許される代わりに、メモリ領域の管理責任を負うという仕組みでした。このことから、所有権とは値そのもの、メモリ領域そのものとも言えると筆者は思っています。

 スマートポインタunique_ptrは、初期値込みでnew演算子も使わずに安全に初期化できるstd::make_unique<T>関数を使って宣言するのでした。make_unique<T>関数は、文字通り所有権を保有するインスタンスを1個しか認めないスマートポインタを生成します。unique_ptr自体はC++ 11から利用可能ですが、make_unique<T>関数はC++ 14からのサポートです。

リスト smartptr.cpp
auto u_str_ptr = make_unique<string>("Hello, World!!");

 これで、所有権と一対一となるunique_ptrというスマートポインタが出来上がります。型はstringで、値は"Hello, World!!"です。このとき、u_str_ptrは"Hello, World!!"が置かれたメモリ領域を「所有」します。所有しているものは、いつかは手放すときが来ます。いつ来るかというと、通常それはスコープを外れたときです。一般的には、ブロックや関数の出口となります。所有権の喪失とともに、所有していたメモリ領域が自動で解放されます。

リスト smartptr.cpp
{
    auto u_str_ptr = make_unique<string>("Hello, World!!");
    cout << *u_str_ptr << endl;
    …ブロックの出口で所有権を失う。メモリ領域も解放される
}

 では、このブロック内で別のスマートポインタを宣言して、初期値をu_str_ptrにしてみます。代入なので、所有権がコピーされておのおのが異なるメモリ領域を保持するか、あるいは所有権を共有するかのような動きをするように見えますが、実際はコンパイルエラーとなります。それは、unique_ptrがコピーコンストラクタもコピー代入演算子も持っておらず、所有権のコピーを認めていないからです。

リスト smartptr.cpp
{
    auto u_str_ptr = make_unique<string>("Hello, World!!");
    auto u_str_ptr2 = u_str_ptr;	// 代入はコンパイルエラー
}

 スマートポインタunique_ptrにとって、所有権はムーブの対象です。所有権をムーブするには、紹介済みのstd::move関数を使います。move関数によってu_str_ptrが右辺値にキャストされるので、所有権がu_str_ptrからu_str_ptr2にムーブします。u_str_ptrはnullptrとなり、表示させようとするとsegmentation faultなどのエラーとなります。その替わり、新たに所有権を得たu_str_ptr2の表示は問題なく行われます。

リスト smartptr.cpp
{
    auto u_str_ptr = make_unique<string>("Hello, World!!");
    auto u_str_ptr2 = std::move(u_str_ptr);
    cout << *u_str_ptr2 << endl;
    cout << *u_str_ptr << endl;   // segmentation fault
}

所有権を共有できるスマートポインタ[C++11]

 スマートポインタには、所有権を共有できるshared_ptrがありました。std::make_shared<T>関数で作成します。あくまでも共有で、メモリ領域を複製するわけではありません。

リスト smartptr_shared.cpp
auto s_str_ptr = make_shared<string>("Hello, World!!");
auto s_str_ptr2 = s_str_ptr;
cout << *s_str_ptr << endl << *s_str_ptr2 << endl;      // 問題なく2つ表示される
*s_str_ptr = "Hello, C++ World!!";
cout << *s_str_ptr << endl << *s_str_ptr2 << endl;      // 双方とも書き換わる

 smartptr.cppでは代入時にコンパイルエラーとなっていましたが、shared_ptrでは問題なく表示まで実行されています。shared_ptrはコピーコンストラクタとコピー代入演算子を持っているので、代入によって所有権のコピーが実行され、s_str_ptrとs_str_ptr2が同じメモリ領域を所有するものとしてマークされます。片方を変更したら、もう一方も変更されます。

 shared_ptrでも所有権はmove関数を使ってムーブできます。以下のリストでは、s_str_ptrの所有権をs_str_ptr3にムーブしています。ムーブ後でも、s_str_ptr2の所有権は生きていることに注目です。もちろん、s_str_ptrはムーブで無効になっているので表示させようとすると実行時にエラーとなります。

リスト smartptr_shared.cpp
…上記の続き…
auto s_str_ptr3 = std::move(s_str_ptr);
cout << *s_str_ptr2 << endl << *s_str_ptr3 << endl;     // 問題なく2つ表示される
cout << *s_str_ptr << endl;                             // segmentation fault

 共有スマートポインタは、簡単に言うと所有権を持っている変数(左辺値といった方がそれっぽいですね)が幾つあるのか、その情報を持っています。要はカウンタですね。カウンタが0以上である限りメモリ領域を保持し、0になればメモリ領域を解放します。このように、データ本体のためのメモリ領域とは別にカウンタの保持、操作が必要なので、若干メモリを食いますし、生成時や代入時の実行コストも少し増えます。

まとめ

 今回は、Modern C++のキモとも言えるムーブセマンティクスを紹介しました。とっつきにくい用語ですが、ムーブという考え方をC++に導入し、コード実行の効率化を言語仕様として明確にものだというのをお伝えできたのではないかと思います。

 次回は、いよいよC++でも使えるようになったラムダ式と無名関数を紹介します。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Modern C++入門連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook <個人紹介> WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/18574 2023/11/06 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング