Puppyです。 お世話になっています。
Win2000,VC6.0,MFC,MDIで開発してます。
先日、メモリの確保についていろいろと教わりました。
メモリの確保と解放を
char (* pDat)[5][10] = new char[day][5][10];
// いろいろな操作
delete[] pDat;
としています。
このパターンであちこちの関数に使っているのですが
何回か、ここにくると、そのうちに「メモリが不足しています」というウィンドウが
出てしまいます。
上記の方法で、問題ないでしょうか?
また、メモリーリークをどのように見つけたらよいでしょうか?
よろしくお願いします。 m(..)m ペコリ
まず、書式があっているかと、new に対して必ず delete が
行われているかという話しがあると思います。
書式の方は、ぱっと見おかしいところは私にはわからないので、
new delete の対応チェックだけ。
> char (* pDat)[5][10] = new char[day][5][10];
> new_cnt++ ;
> sv_pDat = pDat ;
>
> // いろいろな操作
>
> if( sv_pDat != pDat ) ポインタ異常発見 ;
> delete[] pDat;
> new_cnt-- ;
なんか、かっこわるい方法ですが、これでdelete 漏れと
ポインタ変数の値のくらいはチェックできるんではないでしょうか?
まー、ご参考ってことで。
> char (* pDat)[5][10] = new char[day][5][10];
> // いろいろな操作
> delete[] pDat;
こんなのが、いろいろなところにあったら対応を
チェックするのも面倒ですよね。
メモリリークを防ぐには、newやdeleteを使わないのが最善の方法です。
つまり、メモリの確保と開放を勝手にやってくれるSTLやMFCの
コレクションクラスを使うべきです。
CArray<char,char> array;
array.SetSize(day*5*10);
char (* pDat)[5][10] = (char(*)[5][10])array.GetData();
// いろいろな操作
自作するなら、こんな:p)
class Dayx5x10
{
public:
explicit Dayx5x10(int days):p(new char[days][5][10]){}
~Dayx5x10() { delete[] p ; }
char (*operator[](int i))[10] { return p[i]; }
private:
char (*p)[5][10];
Dayx5x10(const Dayx5x10& o); // コピー禁止
Dayx5x10& operator=(const Dayx5x10& o); // 代入禁止
};
Dayx5x10 data(day);
// いろいろな操作
メモリリークしてる場所を見つけるだけなら、
Debug実行の終了時にリーク場所がアウトプットウィンドウに表示されます。
こんな感じ
Detected memory leaks!
Dumping objects ->
C:\Work\VCPP\memoryleak\memoryleak.cpp(36) : {57} normal block at 0x003444D8,
100 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
この場合memoryleak.cppの36行目で確保したメモリが開放されずにメモリリークしてます。
んさん dairygoodsさん 第3水準さん お返事ありがとうございます。
>CArray<char,char> array;
>array.SetSize(day*5*10);
>char (* pDat)[5][10] = (char(*)[5][10])array.GetData();
このやりかただと pDat[day][5][10] の3次元配列でメモリを確保し
pDat が char * 型になるのでしょうか?
そして解放忘れなどは考えなくっていいんですか?
言い忘れていたんですけどぉ、dayは変数(1-31)です。これも問題なしですか?
>こんなのが、いろいろなところにあったら対応を
>チェックするのも面倒ですよね。
ホントもう、悩んでやんなっちゃいますぅ。(涙)
まず、気持ち悪いのでメモリーリークを見つけてから、
その後で、安全な方法に変更しようと思います。
お返事、お待ちしています。いつもご迷惑をおかけします。 m(..)m ペコリ
> このやりかただと pDat[day][5][10] の3次元配列でメモリを確保し
> pDat が char * 型になるのでしょうか?
> そして解放忘れなどは考えなくっていいんですか?
CArrayは多次元配列を扱えないので、1次元でchar[day][5][10] と
同じサイズのメモリを確保して、3次元配列ポインタにキャストしています。
CArrayは、内部でこっそり配列をnewして保持しており、
変数(array)がスコープ外になったときに、勝手に解放します。
なので、解放忘れを気にする必要はありません。
> CArrayは多次元配列を扱えないので
配列の参照は存在しないというC++の制限ですね。
そのあたりはstd::vectorでも同じです。
std::vector<std::vector<std::vector<char> > > array;
のように,最高次以外もコンテナにしてやるのが基本です。
あとは,boostライブラリのboost::shared_arrayとかboost::scoped_arrayを使えば,
typedef char TYPE[5][10];
boost::scoped_array<TYPE> pDat(new TYPE[day]);
という形で確保できます。
#scoped/shared_arrayにはoperator=が無いので,テンポラリを作成して代入になりますが。
dairygoodsさん お返事ありがとうございます。
不具合の場所が見つかりましたぁ。(^-^;
月のかわりめで day の値と確保する配列のサイズがあっていませんでした。
>CArrayは多次元配列を扱えないので、1次元でchar[day][5][10] と
>同じサイズのメモリを確保して、3次元配列ポインタにキャストしています。
これからCArrayにチャレンジしてみます。
>CArrayは、内部でこっそり配列をnewして保持しており、
ふーむ、ズルいけど、便利なんですね。CArray って。
またわからないことがありましたら、質問しますのでお願いします。m(..)mペコリ
皆様、ありがとうございました。
struct DateData{
char m_Data [5][10];
};
std::vector<DateData> a;
じゃだめなん?
YuOさん PAIさん お返事ありがとうございます。
std::vectorも試してみようと思います。
また、わからなかったら質問させていただきます。
ありがとうございました。m(..)mペコリ