ページ 11

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

Posted: 2011年2月05日(土) 08:53
by わんころ
今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: アップキャストと仮想デストラクタ

Posted: 2011年2月05日(土) 09:01
by わんころ
コードが見やすくなるタグがあったんですね・・・。
書いてから気づきました。
見づらくてスミマセン(汗

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

Posted: 2011年2月05日(土) 13:14
by softya(ソフト屋)
C++の規格上は、基底クラスのデストラクタをvirtualにすれば継承クラスのデストラクタは呼ばれるはずです。
呼ばれないとしたらバグでしょうね。
bcc5.5に詳しくないですが、10年以上前のバージョンですし無料版ですのでバグが放置されているのかも。

ちなみに試してみましが、gccとVC++はちゃんと呼ばれます。

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

Posted: 2011年2月05日(土) 14:23
by Justy
わんころ さんが書きました:LNK2005エラーが出てくるのですが
 このエラーに関してはエラーの詳細がないので細かいことは判りませんが、何かが多重定義されているのだと思います。
 具体的に何が多重定義されているかはエラーコードに書いてあるので確認してみて下さい。

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

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

Posted: 2011年2月05日(土) 18:21
by わんころ
出先から失礼します。
返信遅れてしまってすみません。
色々詳細に書いてなくて本当に申し訳ないです(汗)

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

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

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

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

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

Posted: 2011年2月05日(土) 23:44
by softya(ソフト屋)
私こそ失礼しました。
検証コードと言うか、うっかりやりそうなコードを書いておきます。

コード:

#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
明らかに変数がズレてます。

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

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