• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:コピーコンストラクタが呼び出されていない?)

コピーコンストラクタが呼び出されていない

このQ&Aのポイント
  • クラスを定義してオブジェクトを生成する際、コピーコンストラクタが呼び出されない場合があります。
  • myclassとmyclass&は異なるものであり、代入演算子の影響でコピーコンストラクタが呼び出されないことがあります。
  • コピー元のオブジェクトを変更せずにコピーしたい場合は、コピーコンストラクタを使用する必要があります。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

規格の12.8の15節に載っている,コピーコンストラクタの省略ですね。 ISO/IEC 14882:2003 Programming languages - C++ 12 Special member functions - 12.8 Copying class objects / 15 > When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects. (略) > - in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type, the copy operation can be omitted by constructing the automatic object directly into the function’s return value > - when a temporary class object that has not been bound to a reference (12.2) would be copied to a class object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy 日本語版 (JIS X 3014 プログラム言語C++) の記述だと,以下のようになっています。 12 特殊メンバ関数 - 12.8 クラスオブジェクトのコピー > 処理系は,ある基準が満たされれば,そのクラスオブジェクトのコピーによる構築を省略してよい。これは,このオブジェクトのコピーコンストラクタ 及び/又は デストラクタが副作用を持つ場合も含む。 (略) > -返却値の型がクラスである関数の return 文の式が,関数の返却値の型と同じ cv 修飾なしの型の volatile でない自動記憶域期間のオブジェクトの名前の場合,自動記憶域期間のオブジェクトを直接関数の返却値として構築することで,コピー演算は省略できる > -参照に結合していない一時クラスオブジェクトがそれと同じ cv 修飾なしの型をもつクラスオブジェクトにコピーされる場合,一時オブジェクトを直接そのコピー先に構築することで,コピー演算は省略できる。 前者は関数内のreturn文の話なので,今回は関係しません。 なので後者について,元のコードを見ていきます。 > const myclass operator+(const myclass& rhs); //加算 > myclass mc3(mc1 + mc2); //コピーコンストラクタが呼ばれるはず ・mc1 + mc2は参照に結合していない一時クラスオブジェクトを返す ・mc1 + mc2の一時クラスオブジェクトの型はconst myclassで,新たに構築されるmc3の型はmyclass より,後者の条件を満たします。 この場合,省略は「allowed」 (JISでは「してよい」) なので,コンパイラは省略することも省略しないことも許されます。 # 副作用の有無によらないので,コピーコンストラクタの中で複雑なことをやっていても無視できます。 次に, > const myclass& operator=(const myclass& rhs); //代入 > myclass mc3(mc1 = mc2); ・mc = m1は参照に結合しているクラスオブジェクトを返す ので,後者の条件を満たしません。 コンパイラはコピーコンストラクタの呼び出しの省略が規格に許されていませんから,コンパイラはコピーコンストラクタを呼び出すコードを記述します。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (1)

  • a_kwn
  • ベストアンサー率34% (8/23)
回答No.1

コンパイラに依存する問題な気がします。的外れな解答になったらすいません。 たとえば、次のような実装が行われていて、 myclass::myclass(const int& init_) { // (A) x = init_; } myclass::myclass(const myclass& init_) { x = init_.x; } const myclass myclass::operator+(const myclass& rhs) { return myclass(x + rhs.x); // (B) } で、以下のコードを実行したとき、 myclass mc1(10); myclass mc2 = 20; myclass mc3(mc1 + mc2); // (C) myclass mc3(mc1 + mc2); の実行の時に、上の(B)行の myclass::myclass(10 + 20); // (const int& のほう) が呼ばれた形跡があるのだけど、 ”そのあとに myclass::myclass(const myclass& init_); が呼ばれると思ったが、呼び出されないのはなぜ???” という話でしょうか? もし、そうなら、上の(A) の位置のthisポインタの値と、(C)の行の mc3 のアドレス が一致しているか見てみてください。 (デバッガで追えるならデバッガで、printfがログに使えるのなら) myclass::myclass(const int& init_) { // (A) printf("constructor:0x%p\n", this); x = init_; } myclass mc1(10); myclass mc2 = 20; myclass mc3(mc1 + mc2); // (C) printf("mc3:0x%p\n", &mc3); もし、アドレスが一致しているのなら、コンパイラによる ”局所オブジェクトの削除”の最適化が行われているのでは?と思います。

sirn
質問者

お礼

まさに回答して頂いた説明の内容を確認したところ、その通りの結果となりました。 (B)の所に直接コンストラクタを使用する形で、return myclass(x + rhs.x)と書いたときには、このオブジェクトがmc3のオブジェクトのポインタと同じになりました。 また、(B)を以下のように書くとコピーコンストラクタが呼び出されました。 myclass retv(x + rhs.x); return retv; 今更書いても遅いですが、このプログラムはVisualStudio2008で作成しており、最適化を無効化して実験しました。最適化のオプションを付けると、下のコードでもコピーコンストラクタは呼び出されませんでした。 コンパイラ凄いですね......

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • コピーコンストラクタと、代入オペレータについて

    コピーコンストラクタと、代入オペレータについてわかりやすく教えてください。 本を読んでもなんだかよくわかりません。 例があるとうれしいです。

  • 今,C++のコンストラクタを勉強しているのですが,これの働きと役割がい

    今,C++のコンストラクタを勉強しているのですが,これの働きと役割がいまいちわかりません class const{ public: int a; const(); } const::const() { a = 100; } int main() { const c; cout << " c.a = " << c.a << endl; return 0; } この様な例がのっていたのですが、[const c;]は何を定義しているのでしょうか, [c.a]はどの変数のことをさしているのでしょうか?教えてください。

  • クラスがメンバーとしてクラスを持つ時の宣言について

    自作クラスMainClassがMyClassというクラスをメンバー変数として持つ場合、宣言時に引数をいくつか持つコンストラクタを呼び出そうとすると構文エラーとなります。 class MyClass { public: MyClass(int, int); } class MainClass { private: MyClass myclass(10,10); } このような宣言はできないのでしょうか。 引数なしのコンストラクタは呼び出せているようです。

  • オブジェクトのコピー

    以下のプログラムはVC++6.0(win2k上)で、 オブジェクトのコピーを練習するために書いたものです。 このプログラムは、コンパイルは通りますが、 実行エラーになります。どのように変更すればよいでしょうか? #include <iostream> #include <stdio.h> #include <assert.h> using namespace std; class Player{ int data; Player *p; public: Player(); ~Player(); Player(const Player & x); void SetData(int d){ data = d; } int GetData(){ return data; } //代入演算子 Player & operator=(const Player & x); }; Player & Player::operator = (const Player & x){ p = new Player; //Playerオブジェクトを別の場所に作る *p = *(x.p); //pの指すオブジェクトに、mのpが指すオブジェクトを代入 return *this; } Player::Player(const Player& x){ p = new Player; //Playerオブジェクトを別の場所に作る *p = *(x.p); //pの指すオブジェクトに、mのpが指すオブジェクトを代入 } class App { Player *p[3]; public: App(); ~App(){for(int i=0;i<3;i++)delete p[i];} //pの指すオブジェクトのデータを設定するだけ void SetData(int i1,int i2){ p[i1]->SetData(i2); } void init(){p[0]=new Player;p[1]=new Player;p[2]=new Player;} void copy(){p[0]=p[1];SetData(0,100);} void output(){ for(int i=0;i<3;i++)printf("%d\n",p[i]->GetData()); } }; void main() { App *x=new App; x->init(); for(int i=0;i<3;i++)x->SetData(i,i); x->copy(); x->output(); delete x; }

  • ワイド文字列とコンストラクタ

    ワイド文字列(UNICODE)をクラス化しようとしているのですが、 コンストラクタにconst wchar_t*型の引数を持たせて呼び出すと、 コンストラクタに渡された文字列が消えてしまい、wcslen()でサイズを求めると何故かサイズも0になっています。 class WideString { public: WideString(const wchar_t*); … }; WideString::WideString(const wchar_t* ws) { ::OutputDebugString(ws); // ←文字列が消えてる?表示されない int len = wcslen(ws); // ←これが0 } int main(void) { WideString wStr(L"文字列"); … } これは一体どういうことなのでしょうか。どなたか教えてください。

  • C++のクラス内での2次元配列

    C++学習者です。Windows 10上で、Visual Studio Community 2015 を使って勉強しています。 2次元配列を持つクラスを作ろうとしていますが、クラス定義ファイルの中のプライベート変数部分に2次元配列を定義しようとするとエラーメッセージが出てきます。 自分のソースコードは次のようなもので、最後の int aray[rowSize][colSize]; の部分に赤い波線が出ていて、そこにカーソルを合わせると「静的でないメンバー参照は特定のオブジェクトを基準とする相対参照である必要があります。」というメッセージが出ます。 #pragma once #ifndef DSUBARRAY_H // Double Subscripted Array #define DSUBARRAY_H #include <iostream> using namespace std; class DsubArray { public: friend ostream &operator<<(ostream &, const DsubArray &); // output array friend istream &operator>>(istream &, DsubArray &); // input array DsubArray(const int=1, const int=1); // default constructor DsubArray(DsubArray &); // copy constructor ~DsubArray(); // destructor int &operator()(int, int);// subscript -- lvalue const int &operator()(int, int) const; // subscript -- rvalue DsubArray &operator=(DsubArray &);// assignment bool operator==(DsubArray &) const; // equality check of the two arrays bool operator!=(DsubArray &) const; // inequality check int getRowSize() const; int getColSize() const; private: int rowSize; int colSize; int aray[rowSize][colSize]; }; #endif これをたとえば次のように書き換えると、赤い波線は消えるのですが、今度はコラムのサイズが10に固定されてしまい、コンストラクターでこれと異なる数値を与えるとエラーになってしまうのではないかと心配します。 int aray[ ][10]; どなたか2次元配列の正しい作り方を教えてください。お願いいたします。

  • operator代入演算子のやり方で疑問が

    C++の勉強をしています。 そこでoperatorを使うことをやっているんですが、この演算子、引数を二つつけるとエラーになります。クラス内で定義するとこうなります。 ですがグローバルで定義するとエラーが出力されません。 何故なんでしょうか? 戻り値の関係でしょうか?  class Complex { public: Complex() { } public: // これはエラー、この演算子関数のパラメータが多すぎますと出力される const Complex operator+(const Complex x, const Complex y) { } }; // これはでない・・・ const Complex operator+(const Complex x, const Complex y) { } 上記のプログラムだと、下に書いてある代入演算子は問題ありません。 なぜこうなるんでしょうか? ご教授お願いします。

  • クラス中でコンストラクタを使わず変数に代入をする理由は?

    JAVAやJavascript、最近オブジェクト指向的な記述が可能になったphpなどの言語で次のようなクラス定義が可能です。 <JAVAの場合> class A { public int i1 = 1; public int i2; A() { i2 = 2; } } しかし最近覚えたC++では、例の「public int i1 = 1;」のようなクラスのスコープ内で代入を行うことは出来ず、その場所では「public int i2;」のように宣言のみ、代入はコンストラクタで行うようになっています。この場合はなぜ「public int i1 = 1;」はダメなのかと疑問に思っても「そういう仕様だ」と言われれば納得できます(やり方が1つだけなので)。 ではJAVAなどの言語で、例のような場所で「public int i1 = 1;」のような宣言と代入を行うことは、代入をコンストラクタを用いて行うこととどのような違いがあるのでしょうか?また、どちらが推奨されるのでしょうか?

  • 演算子オーバーロードのプログラムで

    今、C++でテンプレートクラスや演算子オーバーロードを使ったプログラムの練習をしているのですが、どうしてもうまく動かなく質問させてもらうことにしました。 大まかに言えば、int型のvalueを持ったDataというクラスを用意し、演算子+と-のオーバーロードによって、クラスの足し算引き算を可能にしよう。ということなのですが、 それぞれのオーバーロードした演算子ともに、addSubtractという+と-両方に対応した関数を呼ぶことで使おうと思っています。 コードは以下のようになっています。 1 #include <iostream> 2 3 using namespace std; 4 5 template<class Comparable> 6 class Data { 7  public: 8   Data(){value = 0;} 9   Data(int i){value = i;} 10   int value; 11   Data<Comparable> operator+(const Data<Comparable> &); 12   Data<Comparable> operator-(const Data<Comparable> &); 13   Data<Comparable> Data<Comparable>::addSubtract(const Data<Comparable> & rhs, int sign); 14 }; 15 16 template<class Comparable> 17 Data<Comparable> Data<Comparable>::addSubtract(const 18 Data<Comparable> & rhs, int sign) { 18   int value1=this->value; 19   int value2=rhs.value; 20   Data<int> result(value1+(sign*value2)); 21   return result; 22 } 23 24 template<class Comparable > 25 Data<Comparable> Data<Comparable>::operator+(const Data<Comparable> & rhs) { 26   return addSubtract(&rhs, 1); 27 } 28 29 template<class Comparable > 30 Data<Comparable> Data<Comparable>::operator-(const 31 Data<Comparable> & rhs) { 31   return addSubtract(&rhs, -1); 32 } 33 34 int main() { 35   Data<int> matrix1(2); 36   Data<int> matrix2(1); 37   cout<<"data1:"<<matrix1.value<<endl; 38   cout<<"data2:"<<matrix2.value<<endl; 39 40   Data<int> result1 = matrix1+matrix2; 41   cout<<"data1+data2"<<result1.value<<endl; 42 43   Data<int> result = matrix1-matrix2; 44   cout<<"data1-data2"<<result2.value<<endl; 45 } このコードを実行したところ、以下のようなエラーメッセージが表示されて動かすことができません。 add.cpp:41: instantiated from here add.cpp:26: error: invalid conversion from `const Data<int>*' to `int' add.cpp:26: error: initializing argument 1 of Data<Comparable>::Data(int) [with Comparable = int]' アドバイスをいただけるとありがたいです。

  • C++のクラスの可変引数化を禁止する方法。

    C++のクラスの可変引数化を禁止する方法。 クラスのインスタンスをprintfの引数にするのをコンパイル・エラーにする方法はないでしょうか? コピーコンストラクタや代入のオーバーロードをprivateにしてもエラーが出ないので方法を探しています。 #include "stdafx.h" class CTest { private: int intdata; public: CTest() : intdata(0) { }; private: void operator =(const CTest& src) {} CTest(const CTest& src) {} }; int _tmain(int argc, _TCHAR* argv[]) { CTest ctest; printf( "%s %p\n", ctest, ctest ); return 0; }