皆様方ご無沙汰しております。初歩的な質問で申し訳ありません。
Vista-Ultimate, VC++2008です vectorでオブジェクトを格納するのに下記のようなのを
書きました。
#include <iostream>
#include <vector>
using namespace std;
class A {
static int i;
int _i;
public:
A() {
cout << i << is born.\n;
_i = i++;
}
~A() {cout << A is deletinng.\n;}
void print() {cout << _i*_i << endl;}
};
int A::i = 0;
int main() {
vector <A*> groupA;
vector <A*>::iterator it;
for (int i=0; i<10; ++i) {
groupA.push_back(new A());
}
for (it = groupA.begin(); it != groupA.end(); ++it) {
(*it)->print();
}
char ch;
cin >> ch;
return 0;
}
質問は
(1)実行すると、Aのdestructorが呼ばれていないようですが、vectorに格納する場合に
は、
A *p = new A();
groupA.push_back(p);
delete p;
のように分けて書かないといけないのでしょうか?
(2)VC++でメモリーリークを検出するのにはどのようにすれば良いのでしょうか?
ご指導宜しくお願いします。
まずそもそも vector (に限らず STL コンテナ) にポインタ値を入れるという設計が
あまりよくない (多態が必要な場合を除く) ので、その辺から再検討。
提示 (1) は
A* p=new A;
A* q=p;
delete p;
と同じことをやっているわけで delete p; の後はもう q を使うことができない。
(2) は、そもそもメモリーリークって何を意図してる?というあたりが明確でないので
答えにくいかな。
# 提示例 class A はバグっているというか、意味がよくわからないのでパスね
> (1)実行すると、Aのdestructorが呼ばれていないようですが
呼ばれるワケがないので、mainの最後で明示的にdeleteします。
for (vector <A*>::iterator it = groupA.begin(); it != groupA.end(); ++it) {
delete *it;
}
C++0X準拠(VC++2010)になれば vector<shared_ptr<A>> でしょね。
皆様方ご教示ありがとうございます。感謝しています。
tetrapod様
STLコンテナにポインタを入れるのは良くない、というのは何となく分かっていましたが、
そうなのですね。ありがとうございます。class Aはただのdummyです
メモリーリークの検出については、以前VC++で別のもう少しきちんとしたプログラム
(bitmapを用いたりするもの)を書いた時、debug modeで走らせたところ、最後にdebug
screenに memory leak was detectedと表示され、ポインタの解放忘れに気付いたのです
が、今回のものでは全く何も表示されず、プログラムが正しいように終了したからです。質
問が明確でなく申し訳ありません。
επιστημη様
やはり呼ばれる訳無いのですね。何となくvectorに期待していましたので・・・ そうです
ね、お示しされましたようにもう一度iteratorを使ってdeleteすれば良いのですね、こんな
ことも分からないなんて本当に馬鹿でした。お手を煩わせて申し訳ありませんでした。
回答ではありませんが、一般に配列クラスに格納している
要素がnewしたオブジェクトであることが保障されている場合、
それ専用に派生させた配列クラスのデストラクタで全ての
配列要素ををdeleteするってのは常用してます。
以降、この配列クラスから派生させたクラスの要素を削除する
コードは不要になるので楽チン。
ただし、newしたオブジェクトのポインタの「コピー」の配列は
削除しちゃいけない場面が想定できるので注意が必要ですが(vv;)。
↓VC9(2008)ならさっくり動きます。お試しあれ。
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
namespace std { using namespace std::tr1; }
class A {
static int i;
int _i;
public:
A() {
cout << i << is born.\n;
_i = i++;
}
~A() {cout << A is deletinng.\n;}
void print() {cout << _i*_i << endl;}
};
int A::i = 0;
int main() {
vector<shared_ptr<A>> groupA;
for (int i=0; i<10; ++i) {
groupA.push_back(shared_ptr<A>(new A()));
}
for (vector<shared_ptr<A>>::iterator it = groupA.begin(); it != groupA.end
(); ++it) {
(*it)->print();
}
}
仲澤@失業者様
そうか、配列クラスを作ってそこのdestructorの中でdeleteするというのも(作るのは難し
そうですが)なかなか魅力的な気がします。今度挑戦してみます。
επιστημη様
shared_prtを用いてdeleteせずに済ます方法ご教授ありがとうございます。
> そうか、配列クラスを作ってそこのdestructorの中でdeleteするというのも(作るのは
難し
> そうですが)なかなか魅力的な気がします。今度挑戦してみます。
そんなに難しくないですよ。・・・テンプレート使えば。
stlはうっとおしいので、MFCのCArryから派生させると
template< typename T >
class ARY_with_AutoDel
: public CArray< T*>
{
private: BOOL m_AutoDel; // 自動削除
public:
ARY_with_AutoDel() : CArray< T*>(){
m_AutoDel = TRUE;
}
public:
virtual ~ARY_with_AutoDel(){ Destroy();}
void AutoDelete_Set( BOOL ex_flg){ m_AutoDel=ex_flg;}
void Destroy(){
if( FALSE == m_AutoDel){
RemoveAll();
return;
}
int num = int( this->GetCount()); // 要素数
T* t;
for( int i = num - 1 ; i>= 0 ; i--){
t = ( *this)[ i];
if( t) delete t;
( *this)[ i] = NULL;
}
RemoveAll();
}
};
class HOGE{
public:HOGE(){}
virtual ~HOGE(){} // virtualである必要がある
};
だったとき、HOGEの自動削除配列は
typedef ARY_with_AutoDel<HOGE> HOGE_ARY; // 配列クラスの定義
HOGE_ARY HogeAry; // 配列のインスタンス
// 配列に要素を追加
HogeAry.Add( new HOGE); // このobjは配列破棄時に自動的にdeleteされる
てな感じ。簡単ですよねぇ。えと、ソース検証してないのでよろしく。
ちなみに適切に使ってる分にはHOGEの子孫群のコンポジットな配列でも、
ちゃんとdeleteしてくれます(デストラクタがvirtualは必須)。
仲澤@失業者様
ご指導ありがとうございます。とっても難しく感じるのですが、理解できるように頑張りま
す。
全然違う話ですみませんが、STLにポインタを入れるのがよろしくないのは
なぜでしょう?自分はとてもよく使っています。
理由としてはOperator=の実装が要らないからだったり特定画面のWndハンドルだったり
するからなのですが・・・
僕もそんなに忌み嫌うほどのこっちゃない思います。
問題は追加/削除および共有が起こったときにややこしくなるってとこですから。
共有とかはクラスにコンテナ押し込めてconst返したり
シングルトンとか使いますねぇ…
ネットワークでどうなるかはやったことないからわかりませんが
共有が起こった時にそれをちゃんと認識した上で管理していないと
削除した時に問題が怒ると言う部分が一番面倒でしょうねぇ。
STLから直接参照している分には良いのでしょうけれど、
STL内に格納されているポインタが別の場所に一人歩きしてしまうと
STLから削除する時にうかつに削除できなくなります。
その部分が一番問題なんじゃないかと思います。
きっちりかっちり管理されていれば問題なさそうですけどね。
もしくはsharedptrで被せてSTLに突っ込むのが一番無難そう。
うわー。
誤)
削除した時に問題が怒ると言う部分が一番面倒でしょうねぇ。
正)
削除した時に問題が起こると言う部分が一番面倒でしょうねぇ。
まあ、使っているところを勝手に削除されたら怒るでしょうけれど。(^^;
> STLにポインタを入れるのがよろしくないのは
> なぜでしょう?
ポインタはバグの宝庫。なので、
STLはコンテナにオブジェクトそのものを格納出来るのに、わざわざバグが発生しやすい
ポインタを使うことはないんじゃないの?くらいでは?
> sharedptrで被せてSTLに突っ込むのが一番無難そう。
御意。