• 締切済み

◆コピコンを実装しないと、returnでエラー?

ポインタを持ったクラスで、 コピーコンストラクタを実装していないと、 newを使用した際にエラーになるそうですが、 なぜなのでしょうか? 値渡しの関数などで、 コピーコンストラクタを実装していないと、 デフォルトコピーコンストラクタで、ポインタ型のメンバ変数の値もコピーするため、 return する前に、 「一時的な変数としてコピーされていた値渡しの引数」が、 関数の終了時に解放され、 「アドレスを指しているポインタ」が指し示す先の領域に「delete」が走ってしまうため、 呼び元の変数で持っているポインタが指し示す先の領域も、 解放されてしまうとかでしょうか?? そもそも、 ポインタ pMem=NULL は、アドレスの指し示す値のリセットで、 Delete pMem は、アドレスが指し示す先頭から、そのクラスで必要としている分のメモリ量進んだアドレスまでを、解放する。 ということで合っているでしょうか? それとも、 「ポインタ」を削除しても、ポインタの指し示す先のアドレス自体は存在していて、 「呼び元」のアドレス格納用領域と、 「関数の呼び出し時にコピーコンストラクタで作られるアドレス格納用領域」は別であり、 ポインタ自体を削除しても、ポインタの先にあるメモリ領域は残っているのでしょうか?

みんなの回答

  • weavaest
  • ベストアンサー率15% (157/1020)
回答No.3

関数の引数にインスタンスの実態を渡した場合には、コピーコンストラクタが実行されます。ですから、関数から抜ける際にはデストラクタが実行されます。 コピーコンストラクタで、コピー元のポインター値がそのままコピー先のポインター値に書き写されるなら、コピー元、コピー先の2つのインスタンスのポインター変数は同じ領域を指すことになりますよね。 デストラクタに、ポインター変数の指す領域を開放する処理があるなら、そこでコピー元のインスタンスが持つポインターが開放されてしまいますね。 ポインター変数にNULLを設定した場合に起きることは、ポインター値がNULLになるだけです。確保された領域が開放されるわけではありません。 deleteはnewで確保された領域を開放するものです。

TeferiMage
質問者

お礼

お返事遅れてしまいすみません! ありがとうございました!

回答No.2

> ポインタを持ったクラスで、 > コピーコンストラクタを実装していないと、 > newを使用した際にエラーになる という状況はこれで作れていますでしょうか? class A { int* i; }; int main(void) { A* a = new A; delete a; return 0; } FreeBSD 10.1上のclang 3.4.1でコンパイルしてみましたが、A::iを使っていないという警告くらいしか見ませんでした。違う場合やエラーにする処理系がある場合、補足してください。 ポインタ変数にNULLを代入するという操作はポインタ変数の値をNULLにしただけで、その指し先が消えるわけではありません。newで確保したものの場合、誰かが明示的にdeleteをしないと確保されたメモリーは解放されません。確保されたメモリーが解放されないと、誰も使っていない領域でメモリーが埋め尽くされ、最終的にメモリーが足りなくなるメモリーリークという不具合に悩まされることになります。 C++だとコーディング規約が許すなら、STL (標準テンプレートライブラリー)を使い、自分でポインタの管理をするのを最小限にします。たとえば、スマートポインターを使うと、たとえば関数の終わりなどスコープを抜けた時点でポインタ変数の指す先は自動的にdeleteされます。あとはvectorの使い方にも慣れるとよいでしょう。 > ポインタ自体を削除しても、ポインタの先にあるメモリ領域は残っているのでしょうか? ポインタ自体というのはポインタを含むオブジェクトを削除しても、ポインターの指し先にあるメモリーが残るかと言うことですよね? はい。一つのオブジェクトを複数のクラスで共有して使用することは普通にあるので、それができないと困ります。この場合も含め、ポインターを使ったプログラムを書く場合、誰がそのポインターの指し先の所有者か(言い換えると、不要になったときに消す義務を持つか)を明確にしないとメモリーリークの温床になります。 参考までに、こんなコードはどうでしょう。 #include <iostream> class A { public: // don't take ownership of i. void SetI(int* i) { i_ = i; } void PrintI() { std::cout << *i_ << std::endl; } private: int* i_; }; int main(void) { A* a0 = new A; A* a1 = new A; int* i = new int; *i = 100; a0->SetI(i); a1->SetI(i); a0->PrintI(); delete a0; // 質問者の考えだとここでiもdeleteされてしまうが、 // 本当はdeleteされないので、a1のi_の指し先はまだ生きている。 a1->PrintI(); delete a1; // ここでiの指し先を削除。 delete i; return 0; } ちなみに、スマートポインターを使うとこのコードがこうなります。 #include <iostream> #include <memory> class A { public: // don't take ownership of i. void SetI(int* i) { i_ = i; } void PrintI() { std::cout << *i_ << std::endl; } private: int* i_; }; int main(void) { std::unique_ptr<A> a0(new A); std::unique_ptr<A> a1(new A); std::unique_ptr<int> i(new int); *i = 100; a0->SetI(i.get()); a1->SetI(i.get()); a0->PrintI(); a0.reset(); // 明示的にa0をdelete a1->PrintI(); a1.reset(); // 明示的にa1をdelete return 0; // iはスコープを抜けたところで自動的にdelete } iをうっかりdeleteし忘れることもありません。

TeferiMage
質問者

お礼

お返事遅れてしまいすみません! ありがとうございました!

  • maiko0318
  • ベストアンサー率21% (1483/6969)
回答No.1

Cの世界では確保したメモリーは開放するまで残っています。 大きなプログラムですと、きちんと開放しないとメモリーが足りなくなって 動かなくなります。 OSによってはプログラム終了でメモリーの解放を行わないものもあるので コンピュータの再立ち上げをするはめに陥ります。 ポインタ pMem=NULL は、アドレスの指し示す値のリセット=○ Delete pMem は、アドレスが指し示す先頭から、そのクラスで必要としている分のメモリ量進んだアドレスまでを、解放する。=○ 「ポインタ」を削除しても、ポインタの指し示す先のアドレス自体は存在していて=× 「関数の呼び出し時にコピーコンストラクタで作られるアドレス格納用領域」は別であり、 ポインタ自体を削除しても、ポインタの先にあるメモリ領域は残っているのでしょうか? =関数を呼び出すとき、ポインターで渡していれば(アドレスを渡しているので) 関数側でポインターを削除すれば戻ってもポインターは使えなくなります。 「値渡しの関数」ということですが、int や charは値渡しで関数を呼び出した時に 違うアドレスにコピーされていますが、ポインターを渡したら関数側で同じアドレスを指します。 値を変更したらメイン側へ戻っても値は変わってしまいます。

TeferiMage
質問者

お礼

お返事遅れてしまいすみません! ありがとうございました!

関連するQ&A