Cpp 右辺値参照 新しいページはコチラ

提供: yonewiki
移動: 案内, 検索
(右辺値参照)
 
1行: 1行:
 +
[[C PlusPlus#C++からの技術|C++]]に戻る
 +
 +
 
<table class="mbox-small" style="border:1px solid #aaa; background-color:#f9f9f9; width:22em;" id="RealTitleBanner">
 
<table class="mbox-small" style="border:1px solid #aaa; background-color:#f9f9f9; width:22em;" id="RealTitleBanner">
 
<tr>
 
<tr>
 
<td style="width:1px;"></td>
 
<td style="width:1px;"></td>
<td class="mbox-text plainlist" style="">本来の表記は「<b><span id="RealTitle" style="font-size:large;">C++ 右辺値参照</span></b>」です。この記事に付けられた題名は{{記事名の制約}}から不正確なものとなっています。</td>
+
<td class="mbox-text plainlist" style="">本来の表記は「<b><span id="RealTitle" style="font-size:large;">C++(Cpp) 右辺値参照</span></b>」です。この記事に付けられた題名は{{記事名の制約}}から不正確なものとなっています。</td>
 
</tr>
 
</tr>
 
</table>
 
</table>
8行: 11行:
 
<br />
 
<br />
 
== '''右辺値参照''' ==
 
== '''右辺値参照''' ==
[[Cpp 参照]]の特殊なパターンとして右辺値参照があります。あまり知られていないという点で特殊と記述しましたが、知っていることからすれば、ごく自然なC++の言語仕様の一部に過ぎないです。
 
  
 +
[[Cpp 参照]]の特殊なパターンとして右辺値参照があります。あまり知られていないという点で特殊と記述しましたが、知っているひとからすれば、ごく自然なC++の言語仕様の一部に過ぎないです。
  
右辺値というとa = b;のように書くとbが方程式の右辺値です。このようなものの参照?って言われてもピンとこないかもしれませんが、よく右辺値にくるものの参照という意味で、オブジェクトをもたいない値の参照というような技術になります。オブジェクトをもたない値というのは具体的には、以下のようなものの右辺にくるものです。
+
 
 +
右辺値というとa = b;のように書くとbが方程式の右辺値です。このようなものの参照?って言われてもピンとこないかもしれませんが、よく右辺値にくるものの参照という意味で、オブジェクトをもたない値の参照というような技術になります。オブジェクトをもたない値というのは具体的には、以下のようなものの右辺にくるものです。
  
 
<syntaxhighlight lang="cpp">
 
<syntaxhighlight lang="cpp">
31行: 35行:
 
rrfnSize Addres->46f85c, rrefnSize Value->256
 
rrfnSize Addres->46f85c, rrefnSize Value->256
 
</syntaxhighlight>
 
</syntaxhighlight>
だから、何?普通のint型と何も変わらないような感じですよね。でもこの参照は右辺値を参照するためのものなので、通常の変数を渡すとコンパイルエラーになります。これははっきりと通常ポインタ変数で扱わないものを型として区別できるとことが、この右辺値参照の活用の道となっています。右辺値というは通常は変数にはとっておかなくていい、一時的な値です。その通常はとっておかなくていいような値を右辺値参照によって取り出そうとする技術です。
+
だから、何?普通のint型と何も変わらないような感じですよね。でもこの参照は右辺値を参照するためのものなので、通常の変数を渡すとコンパイルエラーになります。これははっきりと通常ポインタ変数で扱わないものを型として区別できるとことが、この右辺値参照の活用の道となっています。右辺値というは通常は変数にはとっておかなくていい、一時的な値です。その通常はとっておかなくていいような値を右辺値参照によって取り出して引数として受け取り、それを活用しようとする技術です。
 +
 
 +
<syntaxhighlight lang="cpp">
 +
int nSize;
 +
int&& rrefnSize = nSize; //これは駄目。
 +
</syntaxhighlight>
 +
このようにすると以下のようなコンパイルエラーとなります。
 +
<syntaxhighlight lang="text">
 +
error C2440: '初期化中' : 'int' から 'int &&' に変換できません。
 +
</syntaxhighlight>
 +
VC++なら記述した段階で、不正な代入としてチェックされ、nSizeの文字の下に赤い波線が付きます。
 +
 
 +
 
 +
以下は[[C キャスト | キャスト]]による明示的な右辺値参照代入の指定です。
 +
<syntaxhighlight lang="cpp">
 +
int nSize;
 +
int&& rrefnSize = static_cast<int&&>(nSize); //これはOK。
 +
</syntaxhighlight>
 +
このようにint&&型に[[C キャスト | キャスト]]すれば大丈夫です。これはどういう事かというと、キャストがされると結局は参照が出来るようになるというだけなので、通常の参照と変わらないです。ただ型が右辺値参照で扱われる参照になるので、このあと再度、右辺値を代入することはできて、nSizeの中身が変更されます。ただし、右辺値で無い値を入れることは出来ないので、再度、キャスト付きでしか通常の参照のやり直しをすることはできません。このようなキャストは以下のようなstd::move()というC++標準関数を指定することでも同様の動作を実現できます。
 +
 
 +
<syntaxhighlight lang="cpp">
 +
int nSize;
 +
int&& rrefnSize = std::move(nSize); //キャストと同じで、OK。
 +
</syntaxhighlight>
 +
std::moveは右辺値でない変数も右辺値としてキャストする関数です。型名を書かなくても良いので、記述しやすいというのがこの関数の利点です。右辺値参照型というのは、右辺値のような一時的にメモリに配置されるものの、それ自体は消えてしまって、次に使うことはないような値ですので、このような代入をしたということは、もうその変数での利用はしないということを宣言したようなものですから、元の値は残さなくていいという意味でmoveという名前が付いています。実際にはmove関数で代入した後も元の変数での操作が出来るので、早いところ手動で消滅させたいものですが、動的に生成しなかった変数はプログラム内で宣言した領域内の終了まで残っているので、右辺値とは再利用できないものを扱うものなので再利用しないようにした方がよいでしょう。動的に生成したものを右辺値参照でキャストした場合も元のポインタ変数にヌルを設定するようにします。場合によっては、右辺値参照によって取得した変数を利用したクラスが消滅する際に、元の値も同時に消滅して、次に元の値を保持していたクラスが消滅する際に動作するデストラクタが、元の値のポインタの実体を消滅させる処理をするときに、すでに消滅済で、エラーになる場合があります。元の値のポインタ変数を消滅させる前に右辺値参照として活用するための手順を踏まないとキャストした意味がなくなってしまいますので手順が大事になります。ここでは、右辺値にキャストした値は右辺値として振る舞うために使われるべきなのだということを理解してもらえれば良いと思います。こうした右辺値参照へのキャストの活用方法についてはもう少しあとで触れます。
 +
 
 +
 
 +
以下はキャストを必要としない右辺値参照のパターンです。
 +
<syntaxhighlight lang="cpp">
 +
class CTest
 +
{
 +
public:
 +
  CTest(void){};
 +
  ~CTest(void){};
 +
  int getIntegerNum(void){return 1;};
 +
  static int getIntegerNumStatic(void){return 1;};
 +
};
 +
int Function(){
 +
  return 1;
 +
}
 +
int _tmain(int argc, _TCHAR* argv[])
 +
{
 +
  CTest* pCTestInstance = new CTest;
 +
  int&& rrefnFReturn = Function();//OK。 関数の右辺値参照代入
 +
  printf("rrefnFReturn        Addres->%x, rrefnFReturn        Value->%d\n",&rrefnFReturn,rrefnFReturn);
 +
 
 +
  int&& rrefnFReturn2 = Function();//2回目の関数の右辺値参照代入
 +
  printf("rrefnFReturn2      Addres->%x, rrefnFReturn2      Value->%d\n",&rrefnFReturn2,rrefnFReturn2);
 +
 
 +
  int&& rrefnCReturn = pCTestInstance->getIntegerNum();//OK。クラスメンバ関数の右辺値参照代入
 +
  printf("rrefnCReturn        Addres->%x, rrefnCReturn        Value->%d\n",&rrefnCReturn,rrefnCReturn);
 +
 
 +
  int&& rrefnCReturn2 = pCTestInstance->getIntegerNum();//OK。関数の右辺値参照代入
 +
  printf("rrefnCReturn2      Addres->%x, rrefnCReturn2      Value->%d\n",&rrefnCReturn2,rrefnCReturn2);
 +
 
 +
  int&& rrefnStaticCReturn = CTest::getIntegerNumStatic();//クラス関数(static)の右辺値参照代入
 +
  printf("rrefnStaticCReturn  Addres->%x, rrefnStaticCReturn  Value->%d\n",&rrefnStaticCReturn,rrefnStaticCReturn);
 +
 
 +
  int&& rrefnStaticCReturn2 = CTest::getIntegerNumStatic();//クラス関数(static)の右辺値参照代入
 +
  printf("rrefnStaticCReturn2 Addres->%x, rrefnStaticCReturn2 Value->%d\n",&rrefnStaticCReturn2,rrefnStaticCReturn2);
 +
  return 0;
 +
}
 +
</syntaxhighlight>
 +
出力結果
 +
<syntaxhighlight lang="text">
 +
rrefnFReturn        Addres->32f898, rrefnFReturn        Value->1
 +
rrefnFReturn2      Addres->32f880, rrefnFReturn2      Value->1
 +
rrefnCReturn        Addres->32f868, rrefnCReturn        Value->1
 +
rrefnCReturn2      Addres->32f850, rrefnCReturn2      Value->1
 +
rrefnStaticCReturn  Addres->32f838, rrefnStaticCReturn  Value->1
 +
rrefnStaticCReturn2 Addres->32f820, rrefnStaticCReturn2 Value->1
 +
</syntaxhighlight>
 +
 
 +
上記のように関数の返り値も右辺値として扱われますので、'''返り値の型が一致すれば'''右辺値参照型への代入ができます。構造体やクラスのメンバ関数に対してもキャストすることなく右辺値として参照を受け取ることができます。関数は呼び出される都度、新しいアドレスに処理結果が返って来ています。
 +
 
 +
このような右辺値戻り値を引数にとるような関数で、ポインタ変数のアドレスだけを移動させることによる、付け替え関数を作ることができます。言葉での説明ではイメージできないと思うので、実際にやってみた方がいいですね。
 +
 
 +
Mainプログラム
 +
<syntaxhighlight lang="cpp" line start="1">
 +
#include "stdafx.h"
 +
#include "RightRefSample.h"
 +
 
 +
int _tmain(int argc, _TCHAR* argv[])
 +
{
 +
  printf("★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\n");
 +
  printf("★★開始\n");
 +
  printf("\n");
 +
  printf("★オリジナルのクラス生成 Inst001\n");
 +
  CRightRefSample* crightrefsampleInst001 = new CRightRefSample("001");
 +
 
 +
  printf("\n");
 +
  printf("★2個目のクラス生成 Inst001を初期値でコピーしたクラスInst002\n");
 +
  CRightRefSample* crightrefsampleInst002 = new CRightRefSample(*crightrefsampleInst001);
 +
 
 +
  printf("\n");
 +
  printf("★3個目のクラス生成Inst003\n");
 +
  CRightRefSample* crightrefsampleInst003 = new CRightRefSample("003");
 +
 
 +
  printf("\n");
 +
  printf("★4個目のクラス生成Inst004\n");
 +
  CRightRefSample* crightrefsampleInst004 = new CRightRefSample("004");
 +
 
 +
  printf("★状態表示\n");
 +
  printf("★m_pStrChar:001->%06x 002->%06x 003->%06x 004->%06x\n",
 +
    crightrefsampleInst001->getStrValueAddress(),
 +
    crightrefsampleInst002->getStrValueAddress(),
 +
    crightrefsampleInst003->getStrValueAddress(),
 +
    crightrefsampleInst004->getStrValueAddress());
 +
  printf("★m_pStrChar:001->%6s 002->%6s 003->%6s 004->%6s\n",
 +
    crightrefsampleInst001->getStrValueAddress(),
 +
    crightrefsampleInst002->getStrValueAddress(),
 +
    crightrefsampleInst003->getStrValueAddress(),
 +
    crightrefsampleInst004->getStrValueAddress());
 +
 
 +
 
 +
  printf("\n");
 +
  printf("★3個目に生成したInst003へInst001をコピー\n");
 +
  *crightrefsampleInst003 = *crightrefsampleInst001;
 +
 
 +
  printf("★状態表示\n");
 +
  printf("★m_pStrChar:001->%06x 002->%06x 003->%06x 004->%06x\n",
 +
    crightrefsampleInst001->getStrValueAddress(),
 +
    crightrefsampleInst002->getStrValueAddress(),
 +
    crightrefsampleInst003->getStrValueAddress(),
 +
    crightrefsampleInst004->getStrValueAddress());
 +
  printf("★m_pStrChar:001->%6s 002->%6s 003->%6s 004->%6s\n",
 +
    crightrefsampleInst001->getStrValueAddress(),
 +
    crightrefsampleInst002->getStrValueAddress(),
 +
    crightrefsampleInst003->getStrValueAddress(),
 +
    crightrefsampleInst004->getStrValueAddress());
 +
 
 +
 
 +
  printf("\n");
 +
  printf("★Inst001とInst003を交換\n");
 +
  CRightRefSample::swap(*crightrefsampleInst001,*crightrefsampleInst003);
 +
  printf("★状態表示\n");
 +
  printf("★m_pStrChar:001->%06x 002->%06x 003->%06x 004->%06x\n",
 +
    crightrefsampleInst001->getStrValueAddress(),
 +
    crightrefsampleInst002->getStrValueAddress(),
 +
    crightrefsampleInst003->getStrValueAddress(),
 +
    crightrefsampleInst004->getStrValueAddress());
 +
  printf("★m_pStrChar:001->%6s 002->%6s 003->%6s 004->%6s\n",
 +
    crightrefsampleInst001->getStrValueAddress(),
 +
    crightrefsampleInst002->getStrValueAddress(),
 +
    crightrefsampleInst003->getStrValueAddress(),
 +
    crightrefsampleInst004->getStrValueAddress());
 +
 
 +
  printf("\n");
 +
  printf("★4個目に生成したInst004へInst001をムーブ\n");
 +
  *crightrefsampleInst004 = std::move(*crightrefsampleInst001);
 +
  printf("★状態表示\n");
 +
  printf("★m_pStrChar:001->%06x 002->%06x 003->%06x 004->%06x\n",
 +
    crightrefsampleInst001->getStrValueAddress(),
 +
    crightrefsampleInst002->getStrValueAddress(),
 +
    crightrefsampleInst003->getStrValueAddress(),
 +
    crightrefsampleInst004->getStrValueAddress());
 +
  printf("★m_pStrChar:001->%6s 002->%6s 003->%6s 004->%6s\n",
 +
    crightrefsampleInst001->getStrValueAddress(),
 +
    crightrefsampleInst002->getStrValueAddress(),
 +
    crightrefsampleInst003->getStrValueAddress(),
 +
    crightrefsampleInst004->getStrValueAddress());
 +
 
 +
  printf("\n");
 +
  printf("★生成したインスタンスの全削除手動\n");
 +
  printf("Inst001 Delete\n");
 +
  delete crightrefsampleInst001;
 +
 
 +
  printf("Inst002 Delete\n");
 +
  delete crightrefsampleInst002;
 +
 
 +
  printf("Inst003 Delete\n");
 +
  delete crightrefsampleInst003;
 +
 
 +
  printf("Inst004 Delete\n");
 +
  delete crightrefsampleInst004;
 +
  printf("★★終了\n");
 +
  printf("★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\n");
 +
  return 0;
 +
}
 +
</syntaxhighlight>
 +
RightRefSample.h
 +
<syntaxhighlight lang="cpp" line start="1">
 +
#pragma once
 +
#include <iostream>
 +
 
 +
using namespace std;
 +
 
 +
class CRightRefSample
 +
{
 +
private:
 +
  char* m_pcStrValue;
 +
public:
 +
  int Function(){return 1;};
 +
  CRightRefSample(void);
 +
  CRightRefSample(const char* pconstcDefaultValue);
 +
  CRightRefSample(const CRightRefSample& refcrightrefsampleInst );
 +
  CRightRefSample(CRightRefSample&& rrefcrightrefsampleInst );
 +
  CRightRefSample& operator= (const CRightRefSample& refcrightrefsampleInst);
 +
  CRightRefSample& operator= (CRightRefSample&& refcrightrefsampleInst);
 +
 
 +
  static void swap(CRightRefSample& refcrightrefsampleA,CRightRefSample& refcrightrefsampleB);
 +
  char* getStrValueAddress(){return m_pcStrValue;};
 +
 
 +
  ~CRightRefSample(void);
 +
};
 +
</syntaxhighlight>
 +
RightRefSample.cpp
 +
<syntaxhighlight lang="cpp" line start="1">
 +
#include "stdafx.h"
 +
#include "RightRefSample.h"
 +
 
 +
//コンストラクタ
 +
CRightRefSample::CRightRefSample(void)
 +
{
 +
  printf("%06x: CRightRefSample (void)Constructor        :%s\n", this, __FUNCTION__);
 +
  m_pcStrValue = new char[1024];
 +
  strcpy_s(m_pcStrValue, 1024, "初期値");
 +
 
 +
}
 +
 
 +
CRightRefSample::CRightRefSample(const char* pconstcDefaultValue){
 +
  printf("%06x: CRightRefSample (const char)Constructor  :%s\n", this, __FUNCTION__);
 +
 
 +
  m_pcStrValue = new char[strlen(pconstcDefaultValue) + 1];
 +
  strcpy_s(m_pcStrValue, strlen(pconstcDefaultValue) + 1, pconstcDefaultValue);
 +
}
 +
 
 +
//コピーコンストラクタ
 +
CRightRefSample::CRightRefSample(const CRightRefSample& refcrightrefsampleInst ){
 +
  printf("%06x: CRightRefSample (Ref)CopyConstructor    :%s\n", this, __FUNCTION__);
 +
  m_pcStrValue = new char[1024];
 +
  strcpy_s(m_pcStrValue, 1024, refcrightrefsampleInst.m_pcStrValue);
 +
}
 +
 
 +
//ムーブコンストラクタ
 +
CRightRefSample::CRightRefSample(CRightRefSample&& rrefcrightrefsampleInst ){
 +
 
 +
  printf("%06x: CRightRefSample (RightRef)MoveConstructor:%s\n", this, __FUNCTION__);
 +
  m_pcStrValue = rrefcrightrefsampleInst.m_pcStrValue;//新しいクラスにだけ
 +
  rrefcrightrefsampleInst.m_pcStrValue = nullptr;
 +
}
 +
//=代入演算子の後ろに参照引数でコピー処理
 +
CRightRefSample& CRightRefSample::operator= (const CRightRefSample& refcrightrefsampleInst){
 +
  delete[] m_pcStrValue;
 +
 
 +
  printf("%06x: CRightRefSample (Ref)operator=          :%s\n", this, __FUNCTION__);
 +
  m_pcStrValue = new char[strlen(refcrightrefsampleInst.m_pcStrValue) + 1];
 +
  strcpy_s(m_pcStrValue,strlen(refcrightrefsampleInst.m_pcStrValue) + 1,refcrightrefsampleInst.m_pcStrValue);
 +
 
 +
  return *this;
 +
}
 +
//=代入演算子の後ろに右辺値参照引数でムーブ処理
 +
CRightRefSample& CRightRefSample::operator= (CRightRefSample&& refcrightrefsampleInst){
 +
  delete[] m_pcStrValue;
 +
 
 +
  printf("%06x: CRightRefSample (RightRef)operator=      :%s\n", this, __FUNCTION__);
 +
  m_pcStrValue = refcrightrefsampleInst.m_pcStrValue;
 +
  refcrightrefsampleInst.m_pcStrValue = nullptr;
 +
 
 +
  return *this;
 +
}
 +
//クラス変数の入れ替えムーブ版
 +
void CRightRefSample::swap(CRightRefSample& refcrightrefsampleA,CRightRefSample& refcrightrefsampleB){
 +
  printf("%06x: CRightRefSample (Ref, Ref)swap          :%s\n", 0, __FUNCTION__);
 +
  CRightRefSample crightrefsampleTemp = std::move(refcrightrefsampleA);//ムーブコンストラクタ
 +
  refcrightrefsampleA = std::move(refcrightrefsampleB);//代入演算子ムーブ処理
 +
  refcrightrefsampleB = std::move(crightrefsampleTemp);//代入演算子ムーブ処理
 +
}
 +
 
 +
//デストラクタ
 +
CRightRefSample::~CRightRefSample(void)
 +
{
 +
  printf("%06x: CRightRefSample (void)Destructor        :addres->%06x name->%s\n", this, m_pcStrValue, __FUNCTION__);
 +
  delete[] m_pcStrValue;
 +
}
 +
 
 +
</syntaxhighlight>
 +
非常に長いサンプルになってしまいましたが、こんな感じでClassのメンバ変数を入れ替えるようなプログラム(staticで定義したメンバ関数swap 静的メンバ関数はオブジェクトからでなくても呼べる関数です。別で詳細は説明の予定。thisポインタが使えないクラスの中にあるだけの関数CRightSample::swap(…)のように呼び出すものです。)を作ってみました。算術演算子 = をオーバロード(関数として定義)していまして、(CRightRefSampleのインスタンス) = の後ろに(CRightRefSampleのインスタンス)が設定された場合の関数と(CRightRefSampleのインスタンス)が右辺値参照として設定されている場合の関数の2つをオーバロードしています。同じ代入演算子でも引数が右辺値参照のときはムーブ、引数が参照のときはコピーのように使い分けることができます。右辺値参照の変数は使わなくなる予定の変数ですので、引数で受け取った変数をコピーするのではなくムーブしてしまうようにして利用します。特にswapのような入れ替えではポインタの交換だけを行うことで入れ替えられますので、AとBの入れ替え処理では入れ替え用の一時的な変数に対してAをムーブし、ムーブしおわったAにBの値をムーブ、そして一時的に作った変数からBへムーブという具合に3回のムーブによりポインタの入れ替えができて内容を交換できます。今回はひとつのメンバ変数のみの入れ替えサンプルですが、クラスの中の要素が文字列のような配列であったり、構造体、クラスといったようなもっと複雑なデータを保持している場合にムーブすることでデータの受け渡しをすることができます。コピーを作らなくてもいいような処理にはムーブは資源の節約にもなるし、無駄な処理が無いため処理速度も改善されます。ムーブのような作業は別に右辺値参照を使うことなくできますので、必ずしも右辺値参照が必要なわけではないですが、参照を引数にした場合の処理と分けることができるのが特徴です。今回はstd::moveのような関数であえて右辺値として扱うことでムーブを実現していますが、右辺値参照を引数にしている部分に引数にスタティックなクラスメンバ関数や関数を定義してクラスのインスタンスを戻り値とするような関数を作った場合に純粋な右辺値が登場しますが、そのような場合でも同じようにムーブされて問題ない処理になるはずです。右辺値参照を引数にとるムーブコンストラクタや代入演算子のオーバロードによるムーブを活用できることを知っておくことで、よりよいクラスの使い方が可能となります。同じような関数が多くなりがちですが、このあたりはテンプレートの活用によりさらにコーディングの効率はあげられると思います。ここでは、あまりややこしくならないように最小限のクラス化と、最小限のテンプレートクラスの利用でメモを作成しています。
 +
 
 +
 
 +
ムーブコンストラクタはコピーコンストラクタの引数が通常の参照であったのに対して、元の値をとっておかないような使い方として、引数を右辺値参照型にしているようなコンストラクタになっています。
 +
 
 +
 
 +
このプログラムを理解するにはオブジェクト(インスタンス)、ポインタ、nullptr、文字列操作、参照、クラス(コンストラクタ デストラクタ 静的メンバ関数 演算子のオーバロード オーバロード インライン関数)を理解しておく必要があります。
 +
 
 +
 
 +
サンプルプログラムの出力結果は以下のとおりです。
 +
<syntaxhighlight lang="text">
 +
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
 +
★★開始
 +
 
 +
★オリジナルのクラス生成 Inst001
 +
338240: CRightRefSample (const char)Constructor  :CRightRefSample::CRightRefSample
 +
 
 +
★2個目のクラス生成 Inst001を初期値でコピーしたクラスInst002
 +
3382a0: CRightRefSample (Ref)CopyConstructor    :CRightRefSample::CRightRefSample
 +
 
 +
★3個目のクラス生成Inst003
 +
3382d0: CRightRefSample (const char)Constructor  :CRightRefSample::CRightRefSample
 +
 
 +
★4個目のクラス生成Inst004
 +
338330: CRightRefSample (const char)Constructor  :CRightRefSample::CRightRefSample
 +
★状態表示
 +
★m_pStrChar:001->338270 002->33dbd0 003->338300 004->338360
 +
★m_pStrChar:001->  001 002->  001 003->  003 004->  004
 +
 
 +
★3個目に生成したInst003へInst001をコピー
 +
3382d0: CRightRefSample (Ref)operator=          :CRightRefSample::operator =
 +
★状態表示
 +
★m_pStrChar:001->338270 002->33dbd0 003->338300 004->338360
 +
★m_pStrChar:001->  001 002->  001 003->  001 004->  004
 +
 
 +
★Inst001とInst003を交換
 +
000000: CRightRefSample (Ref, Ref)swap          :CRightRefSample::swap
 +
24f908: CRightRefSample (RightRef)MoveConstructor:CRightRefSample::CRightRefSample
 +
338240: CRightRefSample (RightRef)operator=      :CRightRefSample::operator =
 +
3382d0: CRightRefSample (RightRef)operator=      :CRightRefSample::operator =
 +
24f908: CRightRefSample (void)Destructor        :addres->000000 name->CRightRefSample::~CRightRefSample
 +
★状態表示
 +
★m_pStrChar:001->338300 002->33dbd0 003->338270 004->338360
 +
★m_pStrChar:001->  001 002->  001 003->  001 004->  004
 +
 
 +
★4個目に生成したInst004へInst001をムーブ
 +
338330: CRightRefSample (RightRef)operator=      :CRightRefSample::operator =
 +
★状態表示
 +
★m_pStrChar:001->000000 002->33dbd0 003->338270 004->338300
 +
★m_pStrChar:001->(null) 002->  001 003->  001 004->  001
 +
 
 +
★生成したインスタンスの全削除手動
 +
Inst001 Delete
 +
338240: CRightRefSample (void)Destructor        :addres->000000 name->CRightRefSample::~CRightRefSample
 +
Inst002 Delete
 +
3382a0: CRightRefSample (void)Destructor        :addres->33dbd0 name->CRightRefSample::~CRightRefSample
 +
Inst003 Delete
 +
3382d0: CRightRefSample (void)Destructor        :addres->338270 name->CRightRefSample::~CRightRefSample
 +
Inst004 Delete
 +
338330: CRightRefSample (void)Destructor        :addres->338300 name->CRightRefSample::~CRightRefSample
 +
 
 +
★★終了
 +
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
 +
</syntaxhighlight>
  
  
通常はとっておかなくていいものを残すとは、どういうことなのか具体的なサンプルは以下のとおりです。
+
[[C PlusPlus#C++からの技術|C++]]に戻る

2021年2月6日 (土) 00:00時点における最新版



個人用ツール
名前空間

変種
操作
案内
ツールボックス