コンストラクタ・デストラクタについての質問

このQ&Aのポイント
  • C++言語でのコンストラクタ・デストラクタの使い方について質問です。
  • コンストラクタはオブジェクトが生成されたときに自動的に呼び出される関数であり、デストラクタはオブジェクトが破棄されるときに自動的に呼び出される関数です。
  • コンストラクタでは、オブジェクトの初期化を行うことができます。一方、デストラクタでは、オブジェクトが破棄されるときに必要な後片付けを行うことができます。
回答を見る
  • ベストアンサー

コンストラクタ・デストラクタ

プログラミング言語はC++ C++を触り始めたばかりの素人です。 コンストラクタとデストラクタについて質問です。 下記に参考にしているウェブページから簡潔にしてコードを書いてみました。 コンストラクタ、デストラクタの中はそれぞれに、○○が呼び出されましたと書いてあるだけでよく分からなかったので別のウェブページを見たら コンストラクタは Sample::Sample(){ n=0; } みたいな例があったのですが、このように変数に予め何かの値を代入しておくという事で合ってますか? デストラクタは理解できていません。 下記の例では、どのような処理を書けばいいのでしょうか? #include<iostream> using namespace std; class Sample { private: int n; public: void Show(); Sample(); ~Sample(); }; void Sample::Show() { cout << n << endl; } Sample::Sample() { // n=0; std::cout << "コンストラクタが呼び出されました" << std::endl; } Sample::~Sample() { // どんな処理? std::cout << "デストラクタが呼び出されました" << endl; } main() { Sample sample; sample.Show(); return 0; } 実行結果 コンストラクタが呼び出されました 1     ←コンストラクタ関数内の//を削除で0になることは確認 デストラクタが呼び出されました

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

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

この例だとデストラクターでやる処理は何もないですね。 このクラスから生成されたオブジェクトが消えるときに、消し残したものはできませんので。 たとえば、このクラスの中でnewなどでほかにメモリーを確保している場合、ネットワークの接続をしている場合は、このクラスが消えるときに明示的にそれらの終了処理を書いておかないとそれらのリソースが残るので、デストラクターでの処理が必要になります。 たとえば、 #include<iostream> using namespace std; class Sample { private: int* n; public: void Show(); Sample(); ~Sample(); }; void Sample::Show() { cout << n[0] << endl; } Sample::Sample() { n = new int[1]; // メモリーを動的に確保 for (int i = 0; i < 1; i++) { n[i] = 0; } std::cout << "コンストラクタが呼び出されました" << std::endl; } Sample::~Sample() { delete [] n; // デストラクタで確保したメモリーを解放 std::cout << "デストラクタが呼び出されました" << endl; } int main() { Sample sample; sample.Show(); return 0; } ただ、今時はSTLやスマートポインターを使ってプログラムを書くのがC++では普通だと思いますので、デストラクターで明示的に何かの処理を書くのは珍しくなってきているかもしれません。メンバー変数をvectorやstring、スマートポインターにしてあるとそれらのデストラクターがちゃんとメモリーを解放してくれますし、メモリー周りのバグを減らすためにそういう書き方をするほうが推奨だと思いますので。

tty215
質問者

お礼

hanabutako様へ 質問時に提示したソースコードは、ごちゃごちゃしていたコメントを消して、自分好みに変数名を少し変えただけで処理的には何もいじってなく、ほぼ丸々コピーした状態のものです。 実際に使っている例ではなく、単に書き方を説明しているだけなのかなと思えてきました。 説明の書いてあるページには、newや、他の方にご回答頂いた例のファイルの入出力等の実際に使っている例はなく理解するには難しいものでしたが、提示したソースコードを修正して頂いたおかげで簡潔で分かりやすかったです。 ありがとうございました。

その他の回答 (2)

  • unacyo
  • ベストアンサー率51% (35/68)
回答No.3

コンストラクタはクラスのインスタンスが発生した際に呼び出されます。 主にメンバ変数の初期化に使います。また、初期化時に行いたい処理も書けます。例えば、ミューテックスやセマフォハンドルの生成とか、クラスをのサブルーチンで共通で使うファイルを開くとか。 逆にデストラクタは、インスタンスが消滅する際に呼び出されます。 使っていたハンドルが閉じてなかったら閉じるとか、開いてあるファイルを閉じるとか。 コンストラクタはインスタンスの生成で呼ばれますから、newした時や、newしない変数定義に呼ばれます。 void test() { Sample sam; //samのインスタンス発生なので、コンストラクタ呼び出し Sample* pSam; //pSamのインスタンスはまだ発生してないから、コンストラクタは呼び出さない sam.Show(); pSam = new Sample(); //pSamのインスタンス発生なので、コンストラクタ呼び出し pSam->Show(); delete pSam; //pSamのインスタンス消滅なので、デストラクタ呼び出し return; //samのインスタンス消滅なので、デストラクタ呼び出し }

tty215
質問者

お礼

unacyo様へ ミューテックス、セマフォハンドルの生成・・・例を挙げて頂けたのはありがたいのですが、調べてもよく分かりませんでした。 これらは、メモしておいて今後、理解したいと思います。すみません。 初期化を行うことと呼び出しのタイミングは理解しました。 ありがとうございました。

回答No.1

コンストラクタには前準備(初期化)、デストラクタには後始末を書きます。 たとえば: class testfile { FILE* fp; public: textfile(const char* fname) { fp = fopen(fname,"r"); } ~textfile() { fclose(fp); } ... };

tty215
質問者

お礼

episteme様へ int型の変数を定義するだけじゃデストラクタは必要がないみたいですね。 私の見た例では、提示したソースコードのようにint型の変数を1つ定義してあるだけだったので、理解に苦しみましたが、他の例を挙げてもらえたおかげで理解できたと思います。 ファイルの入出力でコンストラクタとデストラクタを使うのですね。 C言語もやったことはありますが、確かに開いたファイルは閉じろと言われてたのを思い出しました。 こういう前準備と後始末の一連の動作をコンストラクタとデストラクタの関数で行うと、なるほどと思いました。 ありがとうございました。

関連するQ&A

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

  • コンストラクタやデストラクタと例外について

    対処法が「概念的に」書かれているサイトや書籍は結構あるようなのですが、具体的なコードでビシッと書かれてることや、そもそも根本的に「どういう場合に例外が発生するのか」の、帰納的な説明はなかなか見つからないので、もやもやしています。 また、「人が書いたコードを使う場合に全部読んでられないような状況」という前提が暗黙にあって書かれている文章が多いと感じます。 しかし、根本的なところが分からないともやもや感は拭えません。 1.もし仮に自分が全てのコードを把握し、「コーディングの段階で明白に消せる例外の可能性」を、あらかじめ消しておける(さすがにnewなどC++の標準的機能は使用し)とした場合は 自分自身で例外をthrowするか、メモリ不足でbad_allocになる以外にコンストラクタ(あるいはデストラクタ)でcatch出来るような例外が発生する可能性はあるのでしょうか? そして例えば以下のように書いたとします。(必要なヘッダ等は省略してあります) ///////////////////ヘッダ側//////////////////////// class A{ double d; public: A(double a=0) : d(a) {} ~A(void){} }; class B{ std::auto_ptr<A> p; A *alist[10]; public: B(void); ~B(void); }; ///////////////////こちらがソースです//////////////////////// B::B(void) try { memset(&alist,0,sizeof alist); std::auto_ptr<A> ap( new A(.8) ); p=ap; for ( int i=10;i; ) alist[--i]=new A; } catch(std::bad_alloc){ for ( int i=10;i; ) delete alist[--i]; } catch(...) { /*......*/ } B::~B(void){ try{ for ( int i=10;i; ) delete alist[--i]; }catch(...){} } こうしたとすると 2.このコードはコンストラクタ中で例外が発生した場合、コンストラクタなので「呼ばれることになった原因の場所へ自動的に例外が再度投げられる」という点を除けば、安全でしょうか? 3.そもそもコンストラクタで例外が発生した場合、呼び出し元に届くように「自動的に、あるいは手動で」投げなければ「ならない」のでしょうか? 4. 1とかぶりますが「このコードの場合に限定」していうと、catch(std::bad_alloc)の後ろに書いたように、果たしてcatch(...)は必要なのでしょうか? つまりstd::bad_alloc以外の例外が発生する可能性はありますか? 5.このコードの場合では、このようにデストラクタをtryブロックで囲う必要は事実上ないと考えて良いですか? 6.また、もしclass Aのデストラクタを見ることができないという場合は、逆に囲うべき、という意味なのでしょうか? 7.auto_ptrのかわりに通常のポインタを使う場合 ポインタの配列alistと同じように、先にNULLにしておいてからnewして、catch(std::bad_alloc)に入った場合は delete ポインタ。 例外が発生しなかった場合デストラクタのほうにも解放処理はちゃんと書く、としていれば、この場合大丈夫でしょうか?

  • デストラクタについて

     C++初心者です。 delete [] tt ; でデストラクタが要素数だけ実行されるのは 確認しています。 ところがその実行後に、例えば、 cout << tt[3].test() << endl ; としますと、ちゃんと表示されます。 消滅したはずのオブジェクトが使えるのはど うしてでしょうか? どなたかお教え願えませんでしょうか。 どうぞよろしくお願いします。

  • デストラクタについて

    #include <iostream> #include <string> using namespace std; #define NUM 2 //登録人数 class Person{ char *name; int *age; char *hobby; public: Person() { name = new char; age = new int; hobby= new char; } void Set(char *n,int a,char *h) { name=n; *age=a; hobby=h; } char *Get_name(void) { return name; } int Get_age(void) { return *age; } char *Get_hobby(void) { return hobby; } ~Person() { cout<<name<<"のデストラクタ\n"; delete [] name; delete age; delete [] hobby; } }; int main(void) { Person *p; int i; p=new Person[NUM]; p[0].Set("永嶋",21,"映画鑑賞"); p[1].Set("平林",54,"車"); for(i=0;i<NUM;i++){ cout<<"\n名前:"<<p[i].Get_name(); cout<<"\n年齢:"<<p[i].Get_age(); cout<<"\n趣味:"<<p[i].Get_hobby()<<"\n"; } delete [] p; return 0; } というプログラムを作成したのですが デストラクタの3つのdeleteがおかしいようなのですが どのような部分が問題となっているのでしょうか? 回答・アドバイス宜しくお願いいたします。

  • 下記、プログラム内の「char *」の役割

    C++初心者です。 縦長になってしまいますが、 『 #include <iostream.h> void show(int); void show(double); void show(char *);   ←左記の記述の使い方 int main(void) { show(1); show(0.25); show("文字列"); return 0; } void show(int x) { cout << x << endl; } void show(double y) { cout << y << endl; } void show(char *z) { cout << z << endl; } 』 のプログラムにおいて、「char *」の使い方がいまいち理解できません。 上記プログラムですとエラーが表示されないのですが、下記のプログラムだとエラーが発生します。 『 #include <iostream.h> void show(int); void show(double); void show(char);   //←---------上記と違う行 int main(void) { show(1); show(0.25); show("文字列"); return 0; } void show(int x) { cout << x << endl; } void show(double y) { cout << y << endl; } void show(char z) {  //←---------上記と違う行 cout << z << endl; } 』 なぜ、ポインタ(*)を付けないといけないのか分かりやすく教えていただけましょうか。

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

  • [PHP] デストラクタについて教えて下さい

    あるPHPの入門書にデストラクタについての解説があります。 *デストラクタ 「コンストラクタとは反対に、オブジェクトが破棄されるタイミングで実行されるのがデストラクタです。名前は __destructに固定されている。 デストラクタには、クラスの中で使用したリソースを破棄するなど、主に終了するときの処理を記述するのが一般的です。」 とあります。 以下のような記述をしました。(person.php) <?php class Person{ public $lastName; public $firstName;  public function __construct($lastName, $firstName){ $this->firstName = $firstName; $this->lastName = $lastName; } public function show(){ print "<p>私の名前は {$this->lastName} {$this->firstName}です。</p>"; } public function __destruct(){ print "<p>インスタンスが破棄されました。<p>";  } } ?> 上の記述をインスタンス化する記述です。(instancne.php) <?php require_once('person.php'); $p = new Person('田中', '幸太郎'); $p->show(); ?> <表示結果> 私の名前は 田中 幸太郎です。 インスタンスが破棄されました。 となります。試しにもうひとつインスタンスを追加します。 <?php require_once('person.php'); $p = new Person('田中', '幸太郎'); $p->show(); $p1 = new Person('鈴木', '京子'); $p1->show(); ?> <表示結果> 私の名前は 田中 幸太郎です。 私の名前は 鈴木 京子です。 インスタンスが破棄されました。 インスタンスが破棄されました。 説明にある、 「オブジェクトが破棄されるタイミングで実行されるのがデストラクタです。」 「デストラクタには、クラスの中で使用したリソースを破棄するなど、主に終了するときの処理を記述するのが一般的です。」 これらの意味がわかりません。 コンストラクタの 「new 演算子によってインスタンス化されるタイミングで実行される」 「プロパティの初期化」 などの意味は理解できているつもりです。 デストラクタを使うと「オブジェクトが破棄される」、「クラスの中で使用したリソースを破棄する」と ありますが、『一度インスタンスを作るとクラス内のリソースが破棄されるのかな?』と思い ためしにもう一つインスタンスを作成しました。上記の通り問題なく使えています。 膨大な記述になると、クラス、この場合は 「class Person」」は再利用出来なくすると言った意味になるのでしょうか? 他の教材やネットで検索してみたりしたのですが、イメージがつかめません。 以下のPHP公式のサイトから考えてみると、クラス内で参照できるものがなければ、コールされるという風に解釈できるかもしれませんが、意味がわかりません。 デストラクタ(__destruct)はコンストラクタ(__construct)の反対というように記述されているものもありますが余計に意味が分かりません。 初学者にもわかるようにデストラクタについて教えてもらえないでしょうか? またどういうケースで利用するのかもよく分かりません。 そのあたりも併せて教えて下さい。 宜しくお願いいたいます。 (参考) デストラクタ __destruct(): void PHP には、C++ のような他のオブジェクト指向言語に似たデストラクタの概念があります。 デストラクタメソッドは、 特定のオブジェクトを参照するリファレンスがひとつもなくなったときにコールされます。 あるいは、スクリプトの終了時にも順不同でコールされます。 https://www.php.net/manual/ja/language.oop5.decon.php

    • ベストアンサー
    • PHP
  • 条件の配列

    表題の件に付いて質問させていただきます。 複数の条件分岐を簡便に記述する方法を探しております。 簡単な例でいいますと、 int main(){  int value=3;  if(value==0)std::cout <<"value="<<0 <<std::endl;  if(value==1)std::cout <<"value="<<1 <<std::endl;  if(value==2)std::cout <<"value="<<2 <<std::endl;  if(value==3)std::cout <<"value="<<3 <<std::endl; } 上記プログラムの複数の条件分岐をfor文をもちいてかきかえる場合、conditionクラスを作成して class condtion{ private:  int *a;  int b; public  void SetPointer(int* value){a=value}  void SetVal(int val){b=val}  bool isTrue(){   if(*a==b){return true;}   else{ return faulse;}  } } int main(){  int value=3;  condition c[4]; //クラス  for(int i=0;i<4;i++){   c[i]->SetPointer(&value);   c[i]->SetVal(i);  }  for(int i=0; i<4; i++){   if(c[i].isTrue())std::cout<<"i="<<i<<std::endl;  } } と実装できそうな気がするのですが、(上記プログラムは動作確認しておりません。) 変数が増えたり、条件が複雑になると少々繁雑になってしまいそうな気がしております。 もっと簡便に条件分岐を配列として処理する方法は存在しますか? 関数ポインタなどがつかえるのでしょうか? アドバイスよろしくお願いいたします。

  • switch文のエラーについて

    次のプログラムを実行したら slect.cpp:In member function `void select::setBlood(std::string)': slect.cpp:16:error: switch quantity not an integer が出ました。switch文の何が不完全ですか?教えてください。 #include<iostream> #include<string> using namespace std; class select { private: string blood; public: void setBlood(string b); void show(); }; void select::setBlood(string b){ blood=b; switch(b){ case 'A': cout<<"A is best"<<endl; break; case 'B': cout<<"B is best"<<endl; break; case 'AB': cout<<"AB is best"<<endl; break; case 'O': cout<<"O is best"<<endl; break; default: cout<<"crazy"<<endl; break; } } void select::show(){ cout<<"Your blood type is :"<<blood<<endl; } int main(){ string b; select Q; cout<<"Please Input your Blood type in A or B or AB or O:"<<endl; cin>>b; Q.setBlood(b); Q.show(); return 0; }

  • 共用体のサンプルコード : 内容がわかりません

    #include <iostream> using namespace std; union bits{ bits(double n); void show_bits(); double d; unsigned char c[sizeof(double)]; }; bits::bits(double n) { d = n; } void bits::show_bits() { int i, j; for(j = sizeof(double)-1; j>=0; j--){ cout << "バイト単位のビットパターン" << j << ":"; for(i=128; i ; i >>=1) if(i &c[j]) cout <<"1"; else cout << "0"; cout <<endl; } } int main () { bits ob(1991.829); ob.show_bits(); return 0; } このコードが何をしているのか解説していただけないでしょうか?

専門家に質問してみよう