• ベストアンサー
  • 困ってます

デストラクタについて

#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がおかしいようなのですが どのような部分が問題となっているのでしょうか? 回答・アドバイス宜しくお願いいたします。

共感・応援の気持ちを伝えよう!

  • 回答数5
  • 閲覧数190
  • ありがとう数7

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

  • ベストアンサー
  • 回答No.5
  • kmb01
  • ベストアンサー率45% (63/138)

No2補足の回答です。 >Setのメソッドの中にある二つのif文は何のために有るのでしょうか? > >初心者が言うのは生意気ですが >デストラクタの部分でdeleteすれば良いのではないのでしょうか? 同じオブジェクトに対して複数回Setが呼び出されたときに、 以前に確保したname,hobby領域を開放して、新しいname,hobbyの領域を確保しています。 これを行わずいきなりname=new char[strlen(n)+1];とやると、 nameが上書きされるため古いnameが開放できなくなります。 同じオブジェクトに対してSetは絶対に1回しか呼ばないならばこの処理は不要ですが、 そういった条件を設けることはあまり望ましくないと思います。

共感・感謝の気持ちを伝えよう!

質問者からのお礼

やっと意味が分りました。 再度回答ありがとうございました。

関連するQ&A

  • 添字範囲エラー送出とデストラクタについて

    添字範囲エラー送出とデストラクタについて 下記のように(1)SiZE=5,(2)num=6 を投入した場合、添字演算子[]関数で(3)添字範囲エラー送出(size=5,I=5)の時、 IdxRngErr例外を発生し、(4)デストラクタを呼んでcatch((5)catch (IntArray::IdxRngErr&)で捕捉される。 質問 IdxRngErr例外を発生により、デストラクタを呼ばれる理由を教えて頂きたい。 *********************************************************************************** main() { int size, num; cout << "要素数:"; cin >> size;   (1) 5を投入 cout << "データ数:"; cin >> num; 、 (2) 6を投入 f(size, num); return 0; }/ *************************************************************************************** //===== 整数配列クラス ======// class IntArray { int size; // 配列の要素数 int* vec; // 先頭要素へのポインタ ~IntArray() { delete[] vec; } // (4)デストラクタよりIdxRngErrがcatch(5)される。 int& operator[](int i) { // 添字演算子[] if (i < 0 || i >= size) throw IdxRngErr(this, i); (3)// 添字範囲エラー送出(size=5,I=5)の時, return vec[i]; } ******************************************************************************************* / //--- 要素数sizeの配列にnum個のデータを代入して表示 --// void f(int size, int num) { try { IntArray x(size); for (int i = 0; i < num; i++) { x[i] = i; cout << "x[" << i << "] = " << x[i] << '\n'; } }    (5)catch (IntArray::IdxRngErr& x) { cout << "添字オーバフロー:" << x.Index() << '\n'; return; i

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

    プログラミング言語は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になることは確認 デストラクタが呼び出されました

  • 多次元配列の new

    多次元配列を new すると、どのような型のサイズの領域の配列が確保されるんでしょうか?たとえば、  int (*a)[2] = new int[3][2]; とすると、  1. 長さ2のintの配列へのポインタ型の長さ3の配列の領域が確保される のか、  2. int[3][2] すなわち、int が 6 つ分の領域が確保される のか。 今まで、「そりゃあ 2 の方だろう」と信じ込んであまり考えずにいたんですが、「コードの型形式からすると 1 の方の解釈でもいいよなぁ」と、ふと思ったものですから、質問させていただきました。 わたしの環境で調べてみると(配列用のハウスキーピング的な余分の領域とか、パディングなどは無視して)、確かに 1 の方なんですか、これで標準準拠なんでしょうかね?^^; XP Home Edition Ver.2002 SP2 cygwin v.1.0.2-1 GNU g++ v.4.1.1 ===== #include <iostream> #include <new> #include <cstdlib> struct A { char a; void *operator new(std::size_t s) { void *p = std::malloc(s); std::cout << "A::new(): " << p << '\t' << s << '\n'; return p; } void operator delete(void *p, std::size_t s) { std::cout << "A::delete(): " << p << '\t' << s << '\n'; if (p) std::free(p); } void *operator new[](std::size_t s) { void *p = std::malloc(s); std::cout << "A::new[](): " << p << '\t' << s << '\n'; return p; } void operator delete[](void *p, std::size_t s) { std::cout << "A::delete[](): " << p << '\t' << s << '\n'; if (p) std::free(p); } }; int main() { std::cout << sizeof(char) << '\t' << sizeof(A) << '\t' << sizeof(A(*)[8]) << '\n'; A *a = new A; std::cout << a << '\n'; A *aa = new A[8]; std::cout << aa << '\n'; A (*aaa)[8] = new A[8][8]; std::cout << aaa << '\n'; A (*aaaa)[8][8] = new A[8][8][8]; std::cout << aaaa << '\n'; delete[] aaaa; delete[] aaa; delete[] aa; delete a; } ===== % ./a.exe 1 1 4 A::new(): 0x870668 1 0x870668 A::new[](): 0x870678 12 0x87067c A::new[](): 0x870688 68 0x87068c A::new[](): 0x8706d0 516 0x8706d4 A::delete[](): 0x8706d0 516 A::delete[](): 0x870688 68 A::delete[](): 0x870678 12 A::delete(): 0x870668 1

その他の回答 (4)

  • 回答No.4

眠い頭で回答してしまったのでちょっと日本語と用語の使い方がおかしいですね。申し訳ありませんでした。 もしコンストラクタのnewが変数の型の初期値で初期化したいなら、 age = int(); のようにするか、初期化リストを使うと良いですよ。 Person() :name (NULL) , age (0) , hobby (NULL) { } それとも、 *Get_hobby()とかをSet()するまえに呼び出したときの保険なら、 Get()の方で判定して return ""; としてやるのもいいかも。ただし、const char*ですね、コレは。 そうでなければ、毎回きちんとSet()が呼び出されるたびにnew/deleteして メモリ管理しなければなりません。(他の方の仰っているstringもそれを自動で やってくれるものです)

共感・感謝の気持ちを伝えよう!

質問者からのお礼

アドバイス有難うございます。 いろいろ勉強できてためになります。

  • 回答No.3
  • hidebu-
  • ベストアンサー率53% (45/84)

まずコンストラクタの Person() { name = new char; age = new int; hobby= new char; } でヒープ領域にメモリ確保している意図がわかりません。 この処理ロジックだと、別にヒープにメモリ確保する必要なしかと。 ------------------------------------- #include <iostream> #include <string> using namespace std; #define NUM 2 //登録人数 class Person{ char *name; int age; char *hobby; public: Person() { } 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"; } }; 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; } ------------------------------------- といった感じですかね。 よくわからないうちにむやみに多用するとリークおこしまくりますよ^^;

共感・感謝の気持ちを伝えよう!

質問者からのお礼

自分でも必要ないとは思っていたのですが 正確には私が書いたように白とは書いてないのですが メソッドの中でnew演算子を使う事が 条件だったのであのようになってしまいました。 今後気をつけます。 回答有難うございました。

  • 回答No.2
  • kmb01
  • ベストアンサー率45% (63/138)

char*型の代入はポインタ値がコピーされるだけで、その指す先がコピーされるわけではありません。 関数呼び出しでも文字列そのものが渡されるわけではなく先頭アドレスが渡されます。 上の例だと p[0].Set("永嶋",21,"映画鑑賞"); としている部分では、メモリ上のどこかに永嶋、映画鑑賞というデータが書き込まれた領域が作成されていて、 その先頭アドレスが例えば100,200だとすると、関数呼び出し部分は p[0].Set(100,21,200); と呼び出されることになります。 呼び出されるSet内のname=n;はname=100;ということで、 nameに入っていたコンストラクタでnewした領域のアドレスは失われます。 そしてデストラクタでdeleteする領域がnewで確保した領域でないためエラーとなります。 また、name = new char;は1文字分しか領域を確保していないという間違いもあります。 解決案はstring型を使うか、以下のようにSet時に領域を確保する方法があります。 class Person{ char *name; int age;//ポインタにする意味がないと思う char *hobby; public: Person() { name = NULL; hobby= NULL; } void Set(char *n,int a,char *h) { if (name) delete[] name; name=new char[strlen(n)+1]; strcpy(name, n); age=a; if (hobby) {delete[] hobby;} hobby=new char[strlen(h)+1]; strcpy(hobby, h); }

共感・感謝の気持ちを伝えよう!

質問者からのお礼

回答有難うございました。 Setのメソッドの中にある二つのif文は何のために有るのでしょうか? 初心者が言うのは生意気ですが デストラクタの部分でdeleteすれば良いのではないのでしょうか? 見当はずれな事を聞いているかもしれませんが 良い機会なので教えていただければ幸いです。 宜しくお願いします。

  • 回答No.1

いろいろまずいと思います。 まず、コンストラクタで new char; などとしているのに、デストラクタで delete [] name; などとなっています。 これはまずいです。 それと、 Set()でnに指定されるのはnewされた文字列へのポインタではなく ヒープのアドレスです。 new / deleteとnew [] / delete [] は対応させないと。

共感・感謝の気持ちを伝えよう!

質問者からのお礼

回答有難うございます。

関連するQ&A

  • メモリ開放の質問ですが

    #include<iostream> using namespace std; void main(void) { int i; char *s,*t; s=new char[9];t=s; for(i=0;i<9;i++)*(s+i)=i; for(i=0;i<9;i++)cout<<(int)s[i]<<endl; delete s; delete t; //error } において2回目のdelete tでエラーになりますが #include<iostream> using namespace std; void main(void) { int i; char *s,*t; s=new char[9];t=s+2; for(i=0;i<9;i++)*(s+i)=i; for(i=0;i<9;i++)cout<<(int)s[i]<<endl; delete t; delete s; } はエラーになりません (s=t+2をs=t+1に変更するとエラーになる) 実際にdeleteは何を行いエラーを引き起こすのでしょうか? エラーになるメカニズムを教えてください

  • scanfの\nの意味

    タイトルの通りです。 #include<stdio.h> struct Person{ char name[100]; char gender; int age; }; int main(void) { struct Person person1; printf("名前は:"); scanf("%s",person1.name); printf("\n年齢は:"); scanf("%d",&person1.age); printf("\n性別は:"); scanf("\n%c",&person1.gender); printf("\n{name=%s,age=%d,gender=%c}",person1.name,person1.age,person1.gender); return 0; } 上記において aaa , 11 , M と入力すると {name=aaa,age=11,gender=M} と表示されますが、 scanf("\n%c",&person1.gender); を scanf("%c",&person1.gender); に変えると {name=aaa,age=11,gender= } となってしまいます。 この理由と、/nの意味を教えてください。

  • C言語 家系図

    問題 構造体personを以下のように仮定する。 struct person { int age; char name[20]; struct person *father; struct person *mother; }; この構造体の表す人の名前、年齢、その人の父親の名前、およびその人の母親の名前を出力する関数 void print_person(struct person *p) を作成せよ。出力の形式は name: 本人の名前 age: 本人の年齢 father: 父親の名前 mother: 母親の名前 となるようにすること。 また、ポインタ father や mother の値が NULL のときには、名前のかわりに unknown と出力するようにせよ。 以上が問題なのですが自分でプログラムを作ってみたところ実行したら、エラーになって矯正終了されてしまいました。 以下が私の作ったプログラムです。 #include <stdio.h> struct person { int age; char name[20]; struct person *father; struct person *mother; }; void set_name(struct person *p, char name[]) { int i; i = 0; while (name[i] != 0) { p->name[i] = name[i]; i++; } p->name[i] = 0; } void print_person(struct person *p) { printf("name:%s", p->name); printf("age:%s\n",p->age); if(p->father != NULL){ printf("father:%s\n",p->father); } else{ printf("unknown"); } if(p->mother != NULL){ printf("mother:%s\n",p->mother); } else{ printf("unknown"); } } int main(void) { struct person me, dad, mom; set_name(&me, "Michael"); me.age = 16; me.father = &dad; me.mother = &mom; set_name(&dad, "David"); dad.age = 38; dad.father = NULL; dad.mother = NULL; set_name(&mom, "Susan"); mom.age = 36; mom.father = NULL; mom.mother = NULL; print_person(&me); print_person(&dad); print_person(&mom); return (0); } どこが違うのか教えていただけないでしょうか?

  • 配列の中を変更

    入力したnameに、入力したageの数だけ文字を進める(例えばnameがOda、ageが12→表示結果がAmp)にする関数を作成しようとしたのですが、やり方がまったくわかりません。 そもそもこのような場合、文字1つ1つに別の配列を使わなければならないのでしょうか? #include <stdio.h> #define N 1 #define NAME 20 typedef struct{ char name[NAME]; char age; } person; int main(void){ int i; person persons[N]; for(i=0;i<N;i++){ printf("name > "); scanf("%s" , persons[i].name); printf("age > "); scanf("%d" , &persons[i].age); } for(i=0;i<N;i++){ printf("name = %s\n" , persons[i].name); printf("age = %d\n" , persons[i].age); } return 0; }

  • delete演算子オーバーロードについて

    delete演算子オーバーロードする際、 デストラクタが呼び出される困っています。 例えば、 void* operator new(size_t size, const char* filename , int line , const char* funcname ); とnew演算子をグローバル定義します。 すると、意図通りnew演算子がCallされ、対応するコントラクタも 問題なくCallされます。 そして、上記new定義にペアとなるdelete演算子もグローバル定義します。 void operator delete(void* pMem, const char* filename , int line , const char* funcname ); 通常どおりdeleteで呼び出してしまうと、標準のdeleteがcallされてしまうため、 #define MYELETE(s) operator delete( (void*)(s) , __FILE__ , __LINE__ , __FUNCTION__ ) 上記のようなカスタムマクロを定義してCallしています。 オーバーロード定義されたアドレスがCallされるところまで、 意図通りなのですが、肝心のデストラクタがCallされません。 型が判明している場合、 単体でデストラクタを明示的に呼び出すことはできますが、 任意のポインタのデストラクタを明示的に呼び出す方法は ありますでしょうか?

  • 構造体配列

    いつもお世話になっています。 今日構造体配列を勉強した際に下の演習をしていたのですが、いまいち基礎的な関数や配列のことやコマンドライン引数のことが理解できていません。一応自分なりにプログラムを書いてみたのですが、エラーがでます。 どなたかアドバイス・ご指摘・解説・例などをおねがいいたします。 問題はコマンドライン引数で人数を指定し,人数分のデータを標準入力(キーボード)から構造体配列に入力し,標準出力(ディスプレイ)に出力するプログラムです。 #include<stdio.h> typedef struct{ char name[100]; int age; double height; double weight; } PERSONAL_DATA1; typedef struct{ int person[10]; }PERSONAL_DATA2; PERSONAL_DATA1 input_personal_data(int num); void output_personal_data(PERSONAL_DATA2 person[10],int num); int main(int argc, char *argv[]){ PERSONAL_DATA1 x; int num,i; char person[10]; if(argc != 2){ printf("Usage ./test number"); return 0; } num=atoi(argv[1]); for(i=0;i<10&&i<num;i++){ person[i]=input_personal_data(); output_personal_data(person,num); return 0; } } PERSONAL_DATA input_personal_data(void){ PERSONAL_DATA1 x; printf("name>"); scanf("%c" ,&x.name); printf("age>"); scanf("%d" ,&x.age); printf("height>"); scanf("%lf" ,&x.height); printf("weight>"); scanf("%lf" ,&x.weight); return x; } void output_personal_data(PERSONAL_DATA2 person[10],int num){ PERSONAL_DATA1 x; printf("name>>%s\n" ,x.name); printf("age>>%d\n" ,x.age); printf("height>>%lf[cm]\n" ,x.height); printf("weight>>%lf[kg]\n" ,x.weight); } またコマンドライン引数で指定したデータファイルAから入力した個人データ集合を構造体配列に取り込み,標準出力(ディスプレイ)と同じくコマンドライン引数で指定したデータファイルBへファイル出力に出力するプログラムというおまけの問題もあります。 こちらの問題のヒントもいただけませんか?

  • クラス内にnewで形成した配列等が含まれる場合

    クラスを関数内に作成した時にそれを実体コピーさせるreturnで返したいんですが、class内newで形成した動的配列があるため、返した後デストラクタが呼ばれるので動的配列の中身が消滅してしまう(させている)のですが、動的配列の消去にデストラクタを使用しないようにするしかありませんか? class a{ public: int *b; a(){b=new int[10];} ~a(){delete b;} //エラー原因 a operator+(a &s){a c;c.b[0] = b[0] + s.b[0];return c;} }; void main() {a x,y;y = x + x;}

  • プロミング(C++)の質問です。

    プログラミングの宿題で、『5人分の年齢,身長,名前の順にキー ボードからファイルに保存するプログラムを作成せよ』という課題 がでました。以下のように書いたのですが、『書き込むファイルはあるが読み込むファイルがない』といわれたのですが、よくわかりません。 どのように改善したらよいのでしょうか? #include<stdio.h> struct person{ int age; float height; char name[40]; }; int main(void){ person a[5]; FILE *fp; if ((fp=fopen("kadai.dat","w"))==NULL){ fprintf(stderr,"File open failed.\n"); return 1; } int t; for(t=0;t<5;t++){ printf("age,height,name \n"); if(scanf("%d %f %s",&a[t].age,&a[t].height,a[t].name)<3); break; printf("%d %f %s \n",a[t].age,a[t].height,a[t].name); fprintf(fp,"%d %e %s \n",a[t].age,a[t].height,a[t].name); } fclose(fp); return 0; }

  • const 回りのエラー?

    以下のプログラムをcygiwn 上でコンパイルすると エラーが出るのですが 何がいけないのかよくわかりません。 メッセージを読んでconst 回りなのかな?とは思っているのですが… よろしくお願いします。 #include<iostream> #include<cstring> #include<cstdlib> using namespace std; class sample{ char *s; public: sample(); sample(const sample &ob); ~sample(){if(s) delete [] s; cout << "Freeing s\n";} void show(){cout << s << "\n";} void set(char *str); sample operator=(sample &ob); }; sample::sample() { s = new char('\0'); if(!s){ cout << "Allocation error\n"; exit(1); } } sample::sample(const sample &ob) { s = new char[strlen(ob.s)+1]; if(!s){ cout << "Allocation error\n"; exit(1); } strcpy(s,ob.s); } void sample::set(char *str) { s = new char[strlen(str)+1]; if(!s){ cout << "Allocation error\n"; exit(1); } strcpy(s,str); } sample sample::operator=(sample &ob) { if(strlen(ob.s)>strlen(s)){ delete [] s; s = new char[strlen(ob.s)+1]; if(!s){ cout << "Allocation error\n"; exit(1); } } strcpy(s,ob.s); return *this; } sample input() { char instr[80]; sample str; cout << "Enter a string: "; cin >> instr; str.set(instr); return str; } int main() { sample ob; ob = input(); ob.show(); return 0; } <コンパイル結果> In function 'int main()': 78:error:no match for 'operator=' in 'ob = input()()' 49:note:candidates are: sample sample::operator=(sample&)

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

    対処法が「概念的に」書かれているサイトや書籍は結構あるようなのですが、具体的なコードでビシッと書かれてることや、そもそも根本的に「どういう場合に例外が発生するのか」の、帰納的な説明はなかなか見つからないので、もやもやしています。 また、「人が書いたコードを使う場合に全部読んでられないような状況」という前提が暗黙にあって書かれている文章が多いと感じます。 しかし、根本的なところが分からないともやもや感は拭えません。 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 ポインタ。 例外が発生しなかった場合デストラクタのほうにも解放処理はちゃんと書く、としていれば、この場合大丈夫でしょうか?