アップキャストと仮想デストラクタ

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
わんころ

アップキャストと仮想デストラクタ

#1

投稿記事 by わんころ » 10年前

今C++を独学で勉強しています。
クラスAの派生であるクラスBの配列を動的に作って、それをクラスAのポインタに渡してdeleteするとVC++でLNK2005エラーが出てくるのですが、その原因がよくわかりません。
以下にコードを書きます。

コードタグで囲ませていただきました。 by softya(ソフト屋)

コード:

// A.h
#ifndef A_H
#define A_H

class A{
public:
A();
virtual ~A();
};
#endif

// B.h
#ifndef B_H
#define B_H
#include "A.h"

class B : public A{
public:
B();
~B();
};
#endif

// A.cpp
#include "A.h"
#include <iostream>
using namespace std;

A::A(){
cout << "A" << endl;
}
A::~A(){
cout << "~A" << endl;
}

// B.cpp
#include "B.h"
#include <iostream>
using namespace std;

B::B(){
cout << "B" << endl;
}
B::~B(){
cout << "~B" << endl;
}

// Main.cpp
#include "B.h"
#include <iostream>
using namespace std;

int main(){
A* tmp = new B[2];
delete[] tmp;
}
VC++でなく、BCC Developerでも試してみたところ、
こちらではコンパイルは通ったのですが、実行結果が
A
B
A
B
~A
~A
となってクラスBのデストラクタが呼ばれませんでした。

メイン関数内の、
A* tmp = new B[2];
delete[] tmp;
という部分を配列ではなく、
A* tmp = new B;
delete tmp;
とすると、当たり前ですがきちんとBのデストラクタも呼ばれうまくいきます。

どうしてこういう結果になるのかわかりません。
どなたかご教授ください。
よろしくお願い致しますm(_ _)m

わんころ

Re: アップキャストと仮想デストラクタ

#2

投稿記事 by わんころ » 10年前

コードが見やすくなるタグがあったんですね・・・。
書いてから気づきました。
見づらくてスミマセン(汗

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

Re: アップキャストと仮想デストラクタ

#3

投稿記事 by softya(ソフト屋) » 10年前

C++の規格上は、基底クラスのデストラクタをvirtualにすれば継承クラスのデストラクタは呼ばれるはずです。
呼ばれないとしたらバグでしょうね。
bcc5.5に詳しくないですが、10年以上前のバージョンですし無料版ですのでバグが放置されているのかも。

ちなみに試してみましが、gccとVC++はちゃんと呼ばれます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
Justy
副管理人
記事: 122
登録日時: 10年前
住所: 神奈川県

Re: アップキャストと仮想デストラクタ

#4

投稿記事 by Justy » 10年前

わんころ さんが書きました:LNK2005エラーが出てくるのですが
 このエラーに関してはエラーの詳細がないので細かいことは判りませんが、何かが多重定義されているのだと思います。
 具体的に何が多重定義されているかはエラーコードに書いてあるので確認してみて下さい。

わんころ さんが書きました:どうしてこういう結果になるのかわかりません。
 そういう使い方は出来ません。
http://blog.livedoor.jp/exiashio/archives/2375363.html
 std::vector<A*>などを検討してみて下さい。

わんころ
記事: 7
登録日時: 10年前

Re: アップキャストと仮想デストラクタ

#5

投稿記事 by わんころ » 10年前

出先から失礼します。
返信遅れてしまってすみません。
色々詳細に書いてなくて本当に申し訳ないです(汗)

まず、softyaさん。
コードタグで囲んでくださってありがとうございます。
デスクトップPCは64bit版のWin7を入れているので、
VC++は、VC++2010の"64bit"版を導入しています。

エラー内容を正確に書きこもうと思って、先程"32bit"版Win7ノートPCでVC++を使いコードを実行してみたら・・・
すんなり実行できました。。
32bit、64bit版では違った結果になることって・・・
(→と思いましたが、「配列をポリモルフィズム的に扱ってはいけない」、んですね。知りませんでした。)

Justyさん。
リンクありがとうございます。
こういう明快な解答を探していました。
この記事は本当に助かります。
今、ロベールのC++入門講座というのを読んでいるのですが、
ひと通り読んだら、この本も是非購入して読んでみようと思います。

お二方とも、ご助言くださいまして本当にありがとうございましたm(_ _)m

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

Re: アップキャストと仮想デストラクタ

#6

投稿記事 by softya(ソフト屋) » 10年前

私こそ失礼しました。
検証コードと言うか、うっかりやりそうなコードを書いておきます。

コード:

#include <iostream>
using namespace std;

// A.h
class A{
private:
	int aa;
	int ab;
public:
	A();
	virtual ~A();
};

// B.h
class B : public A{
public:
	int a;
	int b;
public:
	B();
	~B();
};

// A.cpp
A::A() : aa(0),ab(0) {
	cout << "A" << endl;
}
A::~A(){
	cout << "~A" << endl;
	cout << aa << endl;
	cout << ab << endl;
}

// B.cpp
B::B() : a(0),b(0){
	cout << "B" << endl;
}
B::~B(){
	cout << "~B" << endl;
	cout << a << endl;
	cout << b << endl;
}

// Main.cpp
int main(){
	A* tmp = new B[2];
	((B*)(&tmp[1]))->a=4;
	((B*)(&tmp[1]))->b=3;
	((B*)(&tmp[0]))->a=2;
	((B*)(&tmp[0]))->b=1;
	delete[] tmp;
}
んで、VC++での実行結果です。

コード:

A
B
A
B
~B
0
0
~A
4
3
~B
2
1
~A
0
0
明らかに変数がズレてます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

わんころ
記事: 7
登録日時: 10年前

Re: アップキャストと仮想デストラクタ

#7

投稿記事 by わんころ » 10年前

softyaさん。
上記のコード、変数のアドレスやクラスのサイズも表示できるように書き加えて実行確認しました。
どうズレているのかよくわかりました(^^)
それと、動的に配列を作った場合、deleteすると後方から(A[0],A[1]とあればA[1]から)デストラクタが呼ばれるんですね。知りませんでした。
大変勉強になりました。ありがとうございましたm(_ _)m

閉鎖

“C言語何でも質問掲示板” へ戻る