C++の解放時の動作

このQ&Aのポイント
  • C++の解放時の動作について疑問があるため質問させていただきます。
  • 簡略ですが、上記を走らせた場合、インスタンスは正しく解放されるのでしょうか?一応デストラクタは動作しますが、実際のところ、子クラス分のデータが解放されない気がします。
  • ネット上のサンプルでこういった例を時々目にしますが、この方法は問題ないのでしょうか?
回答を見る
  • ベストアンサー

C++の解放時の動作

お世話になっております。 C++の解放時の動作について疑問があるため質問させていただきます。 class Super{ private: float x, y, z; public: ~Super(){} }; class Child : public Super{ private: int a, b, c; public: ~Child(){} }; int main( int argc, char **argv ){ A *a = new Child: delete a; return 0; } 簡略ですが、上記を走らせた場合、インスタンスは正しく解放されるのでしょうか? 一応デストラクタは動作しますが、実際のところ、子クラス分のデータが解放されない気がします。 ネット上のサンプルでこういった例を時々目にしますが、この方法は問題ないのでしょうか? よろしくお願いいたします。

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

  • ベストアンサー
  • qwertfk
  • ベストアンサー率67% (55/81)
回答No.4

おそらく、懸念されているとおりの問題が発生します。 > 一応デストラクタは動作しますが、実際のところ、子クラス分のデータが解放されない気がします。 とかかれていますが、実際にはその反対で、データは開放されるが、子クラス分のデストラクタが呼び出されません。 下記のコードを実行するとわかりやすいと思います。 class Super{ public: ~Super(){cout << "s" << endl;} }; class Child : public Super{ public: ~Child(){cout << "c" << endl;} }; int main(int argc, char* argv[]) { cout << "A:" << endl; Super* a = new Child(); delete a; cout << endl; cout << "B:" << endl; Child* b = new Child(); delete b; return 0; } ちょっと乱暴な解釈ですが、基本的には継承がある場合はデストラクタは仮想関数にしたほうが良いです。 class Super{ public: virtual ~Super(){cout << "s" << endl;} }; class Child : public Super{ public: virtual ~Child(){cout << "c" << endl;} };

ClickHere
質問者

お礼

ご回答、ありがとございます。 VC++2008上で実行すると、子クラスのデストラクタは実行されていませんが、 メモリリークも検出されませんでした。 デストラクタ実行→メモリ領域解放 の動作順だとばかり考えていたので、いまいちピンときませんね…。 ちゃんと解放されているのなら、いいのですが…。

その他の回答 (6)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.7

ふと思ったんだけど, #5 へのお礼中 「親クラスにデストラクタを定義しないと子クラスのデストラクタが正しく動作しない」 ってどういう意味なんだろう. どんな処理系でどう「正しく動作しない」のかが気になる.

ClickHere
質問者

お礼

>「親クラスにデストラクタを定義しないと子クラスのデストラクタが正しく動作しない」ってどういう意味なんだろう 仮想関数にしない場合、以下の様にしないと継承した際に~Super()が呼ばれない状態です。 class Super{ public: ~Super(){} }; >どんな処理系でどう「正しく動作しない」のかが気になる. x86系PCのWindows上で実行しています。多分、機種依存の問題ではなく、根本的な言語の仕様と思われます。

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.6

もしかして delete a ; // sizeof(SUPER)分のメモリが解放される delete (Child *)a ; // sizeof(Child)分のメモリが解放される とか考えてます? それとも delete a ; // SUPERの private:float x, y, z;は解放されるけど // Childの private: int a, b, c;が解放されない delete (Child *)a ; // Childの private: int a, b, c;も解放される とか考えてますか? 普通、deleteは、newで確保した分を解放します。 C言語のmallocでどんなサイズを指定しても、freeに指定するのはポインタだけですよね? 今回の例では、どちらもデストラクタを呼ぶ意味が無いので、問題ありません。

ClickHere
質問者

お礼

ご回答、ありがとうございます。 >もしかして >delete a ; // sizeof(SUPER)分のメモリが解放される >delete (Child *)a ; // sizeof(Child)分のメモリが解放される >とか考えてます? 大体そんなことを考えていました。 malloc()とdelete、newとfree()の組み合わせが禁止されているので、 もしかしたら、型で決めているのでは?という心配をしていました。 デストラクタが動作しない以外は問題がないのですね。 勉強になりました。

  • wormhole
  • ベストアンサー率28% (1620/5655)
回答No.5

>ちょっと乱暴な解釈ですが、基本的には継承がある場合はデストラクタは仮想関数にしたほうが良いです。 ただ質問者さんのあげられてる例のSuperとChildはデストラクタは定義しなくてもデフォルトデストラクタで十分なんですよね。

ClickHere
質問者

お礼

ご回答、ありがとうございます。 >ただ質問者さんのあげられてる例のSuperとChildはデストラクタは定義しなくてもデフォルトデストラクタ>で十分なんですよね。 親クラスにデストラクタを定義しないと子クラスのデストラクタが正しく動作しないので 定義しています。子クラスは完全に蛇足でした。 確認したところ、確かに仮想関数にすると、両方のデストラクタが動作しました。

回答No.3

> この方法は問題ないのでしょうか? 「この方法」とはどの部分を指していますか?

ClickHere
質問者

お礼

ご回答、ありがとうございます。 子クラスのインスタンスを親クラスの型としてdeleteすることです。 よろしくお願いします。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

この場合各メンバがデストラクタを持たないので, Super にしろ Child にしろそもそもデストラクタを作る必要がありません. なので「意味的にはおかしいけどこの場合に関して言えば結果的に『正しく』動作する」というところかな. 相変わらず「データが解放される」が何を意味するのかは分からん.

ClickHere
質問者

お礼

ご回答、ありがとうございます。 >相変わらず「データが解放される」が何を意味するのかは分からん newでインスタンス化される際に、内部で確保するメモリ領域のつもりです。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

「データが解放される」とはどういう意味でしょうか? 「子クラス分のデータが解放されない気がします」とありますが, 親クラス分のデータが「解放される」かどうかについては全く気にならないんでしょうか? 気にならないとしたら ・解放される ・解放されない のどちらだと考えているのですか? そしてその理由は?

ClickHere
質問者

お礼

ご回答、ありがとうございます。 質問内容にミスがありましたので、補足させていただきました。 それではよろしくお願いいたします。

ClickHere
質問者

補足

ご回答、ありがとうございます。 Super *p = new Child; とした際に、 delete p; と delete (Child*)p; の動作に違いが出るか?という意味合いのつもりです。 補足ですが、質問内容に A *a = new Child; とありましたが、 Super *a = new Child; のミスでした。 それでは、よろしくお願いいたします。

関連するQ&A

  • C++の話です。

    C++の話です。 静的メンバ変数としてクラスを宣言した場合、デストラクタが呼ばれていないようなのですが、呼ぶ方法はありませんか? できれば「new」「delete」を使わずできると理想的です。 分かる方教えていただけると助かります。 以下、サンプルコードです。 「デストラクタが呼ばれました」と出力されない上、デバッガを使って試してみましたが、やはり呼ばれていないようです。 #include<iostream> class Test{   public:     ~Test(){       std::cout<<"デストラクタが呼ばれました"<<std::endl;     } }; class A{   private:     static Test T; }; int main(){   A a;   return 0; }

  • 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; }

  • インスタンス破棄時にメモリが解放されるようにしたい

    C++言語でプログラムを作成しています あるクラスでインスタンス生成時に動的にメモリを割り当てた後 インスタンスが破棄されるまでそれを使用し インスタンス破棄時に解放するにはどうしたらいいでしょうか デストラクタで解放処理を実装すると 明示的にデストラクタが呼ばれた際に解放されてしまい インスタンスが破棄されるまで使用できませんでした

  • C++ クラスをメンバにもつクラスについて

    お世話になります。C++初心者でうまくコードが書けません(><) クラス1と2があり、クラス1のメンバにはクラス2があります。 メインでクラス1をインスタンス化してクラス2のfunc2を呼び出します。 func2ではクラス1のインスタンスから呼び出された場合にクラス1の m_int1を取得します。 Class Class1{ public:  int m_int1;  Class2 m_Class2; }; Class Class2{ public: void func2(); }; void Class2::func2(){  /*ここの記述方法が分かりません*/  /*C1から呼び出されたらC1のm_int1に100を入れる*/  /*以下間違え*/  class1 C2_1;/*別のclass1のオブジェクトなのでこれに入れてもダメっぽい*/  C2_1.m_int1 = 100; } void main(){  class1 C1;  C1.m_int1 = 10;  C1.m_class2.func(); } C1.m_class2.func()の中から呼び出したC1にアクセスする方法が 分かりません(TT)。実体がまだあるのだからアクセスする方法は あると思うのですが・・・ どなたかよろしくお願いします。

  • クラスの設計の問題

    今C++でクラスの設計をしています。 どう設計すれば、いいか分からないので、しっている方に教えていただきたいですが。 class A{ private: int id; public: int getId(); void setId(); }; class B{ private: int id; public: int getId(); void setId(); } class C{ private: int id; public: int getId(); void setId(); } この三つのクラスが、共通な機能があります。また、実装も同じで、 継承を利用して、どう設計すれば、いいですか? よろしくお願いします。

  • C++のクラスについて

    /*以下のコメントがある行では何故、コンストラクタ(class2::class2)を指定出来ないのですか? デストラクタ(class2::~class2)の場合も問題なくコンパイルが通り、実行できます (http://codepad.org/1oJkxjyZ の23行目) 開発環境 Windows XP SP3 コンパイラ:GCC 実行結果 class1のコンストラクタ class2のコンストラクタ aiueoの実行 class2のデストラクタ class1のデストラクタ */ #include<iostream> class class1; class class2; class class1{ public: class1(); ~class1(); private: class2*pointer; }; class class2{ public: class2(); ~class2(); void aiueo(); }; class1::class1(){ std::cout<<"class1のコンストラクタ"<<std::endl; pointer=new class2(); pointer->aiueo(); //aiueoを~class2に置き換えてもコンパイル出来るが、class2だとエラーが出る } class1::~class1(){ delete pointer; std::cout<<"class1のデストラクタ"<<std::endl; } class2::class2(){ std::cout<<"class2のコンストラクタ"<<std::endl; } class2::~class2(){ std::cout<<"class2のデストラクタ"<<std::endl; } void class2::aiueo(){ std::cout<<"aiueoの実行"<<std::endl; } int main(){ class1 test1; return 0; }

  • C言語のシェルプログラミングの課題が分かりません。

    C言語のシェルプログラミングを作れという課題で、以下のように作ったんですが、実行して何度かコマンドを入力した後、exitによって一発で終わらせることができません。どのように書き換えればいいか教えて下さい。 また、他にも書き換えた方がよいと思えるところがあったら是非教えて下さいm(_ _)m #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> #include <sys/types.h> #include MAX_ARGS 10 #include MAX_LEN 100 extern char **environ; void child(int argc, char *argv[MAX_ARGS]); int main(void){ int argc, n = 0; int status; char input[MAX_LEN], *argv[MAX_ARGS], *cp; const char *delim = "\t\n"; while (1){ ++n; printf("$ "); fflush(stdout); if(fgets(input, sizeof(input), stdin) == NULL){ break; } cp = input; for(argc = 0; argc < MAX_ARGS; argc++){ if((argv[argc] = strtok(cp, delim)) == NULL) break; cp = NULL; } if(strcmp(argv[0], "exit") == 0){ exit(0); } pid_t pid = fork(); if(pid == -1){ perror("fork"); exit(1); }else if(pid == 0){ child(argc, argv); }else{ wait(&status); } } return 0; } void child(int argc, char *argv[MAX_ARGS]{ execvp(argv[0], argv); }

  • 関数の引数の書き方。

    AとBのどちらの書き方でもよいのですか? 私が読んだ本(やさしいC)には、 Aパターンのみだったような? Aパターン ------------------ void main(int argc,char *argv[]) { int i; ・・・・ } Bパターン ------------------ void main(argc, argv) int argc; char *argv[]; { int i; ・・・・ } http://ash.jp/db/ora_c.htm

  • 用語の正式名称は?(C++)

    C++のクラスや構造体で、 class TEST { private: int a; //A int func1(); //B protected: int b; //C int func2(); //D public: int c; //E int func3(); //F } struct XXX { int x; //G } というのがあったとします。 //A~//G の正式な読み方はなんでしょうか? たとえば//Bなら、「プライベートメソッド」「プライベートメンバ関数」ですか? ご存知なかた、ご教授おねがいします。 できればその定義が書かれていた出所(URL)もおしえてください。

  • C言語 コマンドラインの文字列を調べる方法

    はじめまして。 C言語を学習しています。 PCのOSはWindows XPです。 参考書に、アプリケーションの動作を指定するオプションを指定する方法として、下記の説明とプログラムが載っています。 以下の点でつまづいており、ご教示をお願い致します。 1、プログラム実行結果画面(コマンドプロンプトのような黒い画面。スクリーンショットを添付しております。)に【-a】と入力しようとしましたが、テンキーの【-】を押した時点で画面が消えてしまいます。 どのように操作すれば、参考書のような実行結果が得られるのでしょうか。 2、【argc--;】の部分で、ここでなぜデクリメントを使うのでしょうか。 3、argvは文字配列へのポインタ変数とのことですが、if文のところの【argv[argc][0]】が何を意味しているのか教えてください。 argv[配列の要素]のような書き方ではなく、argvの横に[]が2つある時点で混乱してしまいます。 どうかよろしくお願い致します。 ●参考書による説明 一般的に、コマンドラインにはファイル名の他に、アプリケーションの動作を指定するオプションを指定することがあります。 例えば、Windowsでファイル名を指定して実行で defrag と指定して起動すると、デフラグが起動して何もせずに終了しますが、 defrag c: と指定すると、Cドライブのデフラグを開始します。 また、defrag c: -a と指定すると、Cドライブの分析結果だけを表示します。 このような使い方は、パソコン上級者にはおなじみのやり方です。 この例では、 c: と -a という2つの文字列がコマンドラインに渡され、それをアプリケーション内で解析して、動作を決定しています。 同様のことは、コマンドラインの文字列を調べれば簡単にわかります。 ここでは、-a と -s というオプションの有無を解析する例です。 ●プログラム #include <stdio.h> int main(int argc,char *argv[]) { while (argc > 0) { argc--; if (argv[argc][0] == '-') { if (argv[argc][1] == 'a') printf("-a オプション\n"); if (argv[argc][1] == 's') printf("-s オプション\n"); } } return 0; } このプログラムに -a -s というオプションを与えて実行した結果は次の通りです。 -a オプション -s オプション 同様にすれば、いくつのオプションにでも対応することができます。 また、先頭が - ではない文字列をファイル名として扱うようにすれば、 ファイル名も前項と同様に取得することができます。

専門家に質問してみよう