c+++のコピーコンストラクションのパラメータタイプは参照でなければなりません。
4903 ワード
C++においては、コンストラクタ、コピーコンストラクタ、コンストラクタ、および割当関数(割当演算子の再負荷)が最も基本的であり、把握する必要がある知識である。しかし、もし私があなたに「コピーコンストラクションのパラメータはなぜ引用タイプを使わなければならないのですか?」この質問にどう答えますか?一回のメモリを減らすためにコピーすると答えますか?恥ずかしいことに、私の第一印象もこのように答えました。でも大丈夫です。考えてみたら、この答えは間違っています。コピーコンストラクタのパラメータが参照でないと、CClassのような形になるからです。class)とは、値を伝える方式を採用することに相当し、値を伝える方式は、この類のコピーコンストラクタを呼び出し、無限再帰的にコピーコンストラクタを呼び出すことになる。したがって、コピーコンストラクタのパラメータは参照である必要があります。明確にする必要があるのは、伝達ポインタも実は伝達値であり、上のコピーコンストラクションがCClass(const CClass*c_)と書かれている場合である。クラスもだめです。事実上、引用だけが伝値の外ではなく、他のすべての伝达方式は伝値です。まず一例から始めます。(自分でこのプログラムの出力を確認してみます。)
以下のいくつかの場合、コピーコンストラクタが呼び出されます。a、明示的または暗黙的に同じタイプのオブジェクトで他のオブジェクトを初期化します。上記の例のように、オブジェクトcでdを初期化する。b、実務として一つの関数に伝達する。CClass(const CClass c_)のようです。class)では、CClassのコピーコンストラクタを呼び出します。c、関数の中でオブジェクトを返すときも、戻り値タイプのコピーコンストラクタを呼び出します。d、シーケンスコンテナの要素を初期化する場合。例えばvector<string>svec(5)は、stringのデフォルトの構造関数とコピーの構造関数が呼び出されます。e、配列要素をリストで初期化する場合。string a={string}、string("world")stringのコピーコンストラクタを呼び出します。構造関数が明示的に宣言されていない場合、コンパイラはデフォルトの構造関数を合成するクラスになります。一つのクラスでコンストラクタを宣言したら、コンパイラがデフォルトのコンストラクタを合成するのを阻止します。コンストラクタとは違って、他のコンストラクタが定義されていても、コンパイラは常にコピーコンストラクタを合成してくれます。また、関数の戻り値は参照であるかどうかにも大きな違いがあります。戻りは引用ではなく、単純なオブジェクトであるため、コピーコンストラクタを呼び出す必要があります。さもなければ、引用であればコピーコンストラクタを呼び出す必要がありません。
#include<iostream>
using namespace std;
class CExample
{
private:
int m_nTest;
public:
CExample(int x) : m_nTest(x) //
{
cout << "constructor with argument"<<endl;
}
// , const ,
CExample(const CExample & ex) //
{
m_nTest = ex.m_nTest;
cout << "copy constructor"<<endl;
}
CExample& operator = (const CExample &ex) // ( )
{
cout << "assignment operator"<<endl;
m_nTest = ex.m_nTest;
return *this;
}
void myTestFunc(CExample ex)
{
}
};
int main(void)
{
CExample aaa(2);
CExample bbb(3);
bbb = aaa;
CExample ccc = aaa;
bbb.myTestFunc(aaa);
return 0;
}
この結果が一目で分かると、おめでとうございます。立ってお尻をひねることができます。もう下を見なくてもいいです。もしあなたの結果と出力結果に誤差があるなら、謙虚に読んでください。最初の出力:constructor with argment // CExample aa(2)もしあなたが理解していないなら、誰かに連れて行って痛打してください。そして口の中で「私は二師兄です。私は二師兄です。」と叫びます。第二の出力:constructor with argment // CExample bb(3)最初の3番目の出力を分析します。assignment operator // bb=aa;第四の出力:copy constructor // CExample ccc=aaこの二つは一緒に置いて話します。なぜ二つが違っているのかという質問が必ずあります。理由は、bbオブジェクトはすでに実用化されています。構造は必要ありません。この時はaaaをbbに割り当てるだけで、賦値関数を呼び出すだけです。こんなに簡単です。まだ分からないなら、壁に突き当たります。しかし、cccはまだ実用化されていませんので、呼び出しはコピーコンストラクションで、cccを作ります。賦値関数ではなく、分かりませんでしたら、壁にぶつかります。5番目の出力:copy constructor // bbb.myTestFun(aa)実際にはaaaがパラメータとしてbb.myTestFnc(CExample ex)に伝達されます。つまりCExample ex=aaです。四番目と一致しています。だからやはりコンストラクションをコピーします。賦値関数ではなくて、まだ分からないなら、頭はさっき血を流しました。もうぶつけないでください。自分で力を入れてもう一度入れてください。この例を通して,なぜコンストラクタのパラメータをコピーするかは参照型しか使えないのかを分析した。4番目の出力を見てください。copy constructor // CExample ccc=aaccc構造で、実質的にはccc.Example(aa)です。コピーコンストラクタパラメータが参照タイプでないと、ccc.Example(aa)をaaaにしてccc.C.Example(CExample ex)に送る。つまりCExample ex=aaは、exが初期化されていないため、CExample ex=aaはコピーコンストラクタに呼び出される。必然的にまたaaaがCExample exに伝えられます。つまりCExample ex=aaaです。コピーコンストラクタをトリガします。このまま永遠に再帰します。だから、あんなに大きな回り道をして、コピーコンストラクタのパラメータを説明したいです。引用のタイプは一回のメモリコピーを減らすためではなく、コピーコンストラクタの無制限な再帰を避けるためです。以下のいくつかの場合、コピーコンストラクタが呼び出されます。a、明示的または暗黙的に同じタイプのオブジェクトで他のオブジェクトを初期化します。上記の例のように、オブジェクトcでdを初期化する。b、実務として一つの関数に伝達する。CClass(const CClass c_)のようです。class)では、CClassのコピーコンストラクタを呼び出します。c、関数の中でオブジェクトを返すときも、戻り値タイプのコピーコンストラクタを呼び出します。d、シーケンスコンテナの要素を初期化する場合。例えばvector<string>svec(5)は、stringのデフォルトの構造関数とコピーの構造関数が呼び出されます。e、配列要素をリストで初期化する場合。string a={string}、string("world")stringのコピーコンストラクタを呼び出します。構造関数が明示的に宣言されていない場合、コンパイラはデフォルトの構造関数を合成するクラスになります。一つのクラスでコンストラクタを宣言したら、コンパイラがデフォルトのコンストラクタを合成するのを阻止します。コンストラクタとは違って、他のコンストラクタが定義されていても、コンパイラは常にコピーコンストラクタを合成してくれます。また、関数の戻り値は参照であるかどうかにも大きな違いがあります。戻りは引用ではなく、単純なオブジェクトであるため、コピーコンストラクタを呼び出す必要があります。さもなければ、引用であればコピーコンストラクタを呼び出す必要がありません。
#include<iostream>
using namespace std;
class A
{
private:
int m_nTest;
public:
A()
{
}
A(const A& other) //
{
m_nTest = other.m_nTest;
cout << "copy constructor"<<endl;
}
A & operator =(const A& other)
{
if(this != &other)
{
m_nTest = other.m_nTest;
cout<<"Copy Assign"<<endl;
}
return *this;
}
};
A fun(A &x)
{
return x; // ,
}
int main(void)
{
A test;
fun(test);
system("pause");
return 0;
}
は筆記試験のテーマを共有して、下図のC++コードをコンパイルして実行します。結果は何ですか?A)コンパイルエラー;(B)コンパイルに成功し、実行中にプログラムがクラッシュしました。(C)コンパイルが正常に動作し、出力10。正しい答えを選んで、原因を分析してください。
class A
{
private:
int value;
public:
A(int n)
{
value = n;
}
A(A other)
{
value = other.value;
}
void Print()
{
cout<<value<<endl;
}
};
int main(void)
{
A a = 10;
A b = a;
b.Print();
return 0;
}
答え:コンパイルエラー。コピーコンストラクタに入ってきたパラメータはAの一例である。値を伝えるので、形を実際にコピーするとコピーコンストラクタが呼び出されます。したがって、コンストラクタの値を複製することができれば、永続的な再帰性を形成し、スタックオーバーフローを引き起こすことができる。したがって、C++の基準は、コンストラクタの値を伝えるパラメータをコピーすることができず、参照または定数参照でなければなりません。Visual StudioとGCCでコンパイルが間違っています。