/*=================================================================
new / delete時にメモリ計測を行うクラス
=================================================================*/
class mem_base {
public:
//---------------------------------------------------------------
// コンストラクタ
//---------------------------------------------------------------
mem_base();
//---------------------------------------------------------------
// メモリ確保
//---------------------------------------------------------------
void *operator new(size_t t);
void *operator new[](size_t t);
//---------------------------------------------------------------
// メモリ解放
//---------------------------------------------------------------
void operator delete(void *p);
void operator delete[](void *p);
}; // end class mem_base
/*===================================================================
自作型名前空間
===================================================================*/
namespace type {
/*=================================================================
双方向循環list:
要素を追加する際に型を指定することで異なる型をリストにできる
=================================================================*/
class list : public mem_base {
protected:
/*---------------------------------------------------------------
listを構成する最小要素
---------------------------------------------------------------*/
class cell_base : public mem_base {
public:
cell_base *next;
cell_base *prev;
cell_base() : next(NULL), prev(NULL), mem_base() {}
}; // end class cell
/*---------------------------------------------------------------
listのデータ部
---------------------------------------------------------------*/
template <class U> class cell final : public cell_base {
public:
U data;
cell() : cell_base(){}
};
/*
リストの先端
*/
mem<cell<tchar> > head;
public:
/*---------------------------------------------------------------
コンストラクタ
---------------------------------------------------------------*/
list() : mem_base(), head(mem<cell<tchar> >())
{
// 循環リストを作成
head.get().prev = head.get().next =
&static_cast<cell_base&>(head.get());
}
/*---------------------------------------------------------------
後端にpush
---------------------------------------------------------------*/
template<class U> void push(const U &n) {
cell<U> *c = new cell<U>;
c->data = n;
c->prev = head.get().prev;
c->next = &static_cast<cell_base&>(head.get());
c->next->prev = c->prev->next = c;
}
/*---------------------------------------------------------------
指定位置をpop
---------------------------------------------------------------*/
void pop(cell_base *p) {
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
}
/*---------------------------------------------------------------
リストの先頭を返す
---------------------------------------------------------------*/
const cell_base *begin() const { return &(cell_base&)head.get(); }
cell_base *begin() { return &(cell_base&)head.get(); }
/*---------------------------------------------------------------
デストラクタ
---------------------------------------------------------------*/
~list(){
auto a = begin();
while(a != a->next) {
pop(a->next);
}
}
/*---------------------------------------------------------------
値を取得する
---------------------------------------------------------------*/
template<class U> void get(
const type::list::cell_base *p,
U &t
){
/*
constを外してダウンキャストしたポインタを左辺値へ格納する
*/
type::list::cell<U> *a =
static_cast<type::list::cell<U>*>(
const_cast<type::list::cell_base*>(
p
)
);
t = a->data;
}
}; // end class list
} // end namespace type
メモリリークを解消したい
メモリリークを解消したい
様々な型を一つのリストに格納できる双方向リストを作りたいと思い、下記のコードを書きました。現在、push()とget()でメモリリークが発生しており、自分では原因がわからず困っています。よろしくお願いします。
検証コード:
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: メモリリークを解消したい
出来れば、コンパイルが通る1つのソースコードとして提供して貰いたいのですが、無理でしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: メモリリークを解消したい
全文はこんな感じです。中身は、main.cc, Type.cc, Type.hppです。
- 添付ファイル
-
- main.zip
- 十分注意してますが、念のためウイルスチェックを各自お願いします
- (3.84 KiB) ダウンロード数: 120 回
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: メモリリークを解消したい
C++11だと言うのは分かるのですが、cygwinなど当方の環境で今のところコンパイルできていません。
VC++2012は起動できないので未調査です。
環境が出来るまでお待ちください。 あと調べてみないと分かりませんが、C++11に詳しくないので答えられないかもしれません。
ちなみに、このコードの環境を教えて下さい。LinuxとかUnixだと思うんですが。
【追記】
※ VC++2012のコマンドラインではコンパイル出来ました。ただし、CRTDBGのレポートが出ませんでした。調査中。
VC++2012は起動できないので未調査です。
環境が出来るまでお待ちください。 あと調べてみないと分かりませんが、C++11に詳しくないので答えられないかもしれません。
ちなみに、このコードの環境を教えて下さい。LinuxとかUnixだと思うんですが。
【追記】
※ VC++2012のコマンドラインではコンパイル出来ました。ただし、CRTDBGのレポートが出ませんでした。調査中。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: メモリリークを解消したい
Mac OS X 10.7 + clang 3.0 + valgrind で試したところ、リークは検出できませんでした。
$ clang++ -std=c++0x *.cc
$ valgrind --leak-check=full ./a.out
==15116== Memcheck, a memory error detector
==15116== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==15116== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==15116== Command: ./a.out
==15116==
--15116-- ./a.out:
--15116-- dSYM directory is missing; consider using --dsymutil=yes
==15116==
==15116== HEAP SUMMARY:
==15116== in use at exit: 2,058 bytes in 32 blocks
==15116== total heap usage: 34 allocs, 2 frees, 2,122 bytes allocated
==15116==
==15116== LEAK SUMMARY:
==15116== definitely lost: 0 bytes in 0 blocks
==15116== indirectly lost: 0 bytes in 0 blocks
==15116== possibly lost: 0 bytes in 0 blocks
==15116== still reachable: 2,058 bytes in 32 blocks
==15116== suppressed: 0 bytes in 0 blocks
==15116== Reachable blocks (those to which a pointer was found) are not shown.
==15116== To see them, rerun with: --leak-check=full --show-reachable=yes
==15116==
==15116== For counts of detected and suppressed errors, rerun with: -v
==15116== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: メモリリークを解消したい
こちらも、VC++2012コマンドラインで
cl main.cc Type.cc /EHsc /D_DEBUG /DEBUG /MDd /Zi /Od
でコンパイルリンクしましたが、実行にリークは検出されていません。
【追記】 環境が明確で無い為、追試験が難しい状況です。
cl main.cc Type.cc /EHsc /D_DEBUG /DEBUG /MDd /Zi /Od
でコンパイルリンクしましたが、実行にリークは検出されていません。
【追記】 環境が明確で無い為、追試験が難しい状況です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: メモリリークを解消したい
/*---------------------------------------------------------------
後端にpush
---------------------------------------------------------------*/
template<class U> void push(const U &n) {
cell<U> *c = new cell<U>;
c->data = n;
c->prev = head.get().prev;
c->next = &static_cast<cell_base&>(head.get());
c->next->prev = c->prev->next = c;
}
/*---------------------------------------------------------------
指定位置をpop
---------------------------------------------------------------*/
void pop(cell_base *p) {
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
}
pop()では、cell_baseとしてdeleteしているので、
少なくともcell_baseクラスには仮想デストラクタが必要だと思います。
Re: メモリリークを解消したい
>softyaさん
環境はVS2012で、OSはWin7です。ビルドはプロジェクトを作成した初期状態のままで行なっています。
>a5uaさん
確かにそうですね、ありがとうございます。
環境はVS2012で、OSはWin7です。ビルドはプロジェクトを作成した初期状態のままで行なっています。
>a5uaさん
確かにそうですね、ありがとうございます。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: メモリリークを解消したい
すいません。私の所ではプロジェクトが作れない → VS2012のIDEが起動できないのでコマンドラインでしか確認できません。
なにかオプションが違うだけだとは思うんですが。
試しにコンソールでビルド実行してみてもらえませんか?
なにかオプションが違うだけだとは思うんですが。
試しにコンソールでビルド実行してみてもらえませんか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: メモリリークを解消したい
cl main.cc Type.cc /EHsc /D_DEBUG /DEBUG /MDd /Zi /Od を実行しましたが、特に何も表示されませんでした。
デバッグ機能がコンソールに表示されてないのかな?と思い、main.ccを以下のように書き換えました
これをコマンドライン上から実行した結果、
Detected memory leaks!
Dumping objects ->
{158}
normal block at 0x0026E310, 8 bytes long.
Data: < & > B8 D0 26 00 00 00 00 00
Object dump complete.
と表示されました。
デバッグ機能がコンソールに表示されてないのかな?と思い、main.ccを以下のように書き換えました
#include"Type.hpp"
#include <vector>
#include <crtdbg.h>
#define _CRTDBG_MAP_ALLOC
int __cdecl myHookFunction(int reportType, char *message, int *returnValue)
{
switch (reportType)
{
case _CRT_ASSERT:
{
printf("%s\n", message);
break;
}
case _CRT_WARN:
{
// メモリリークの場合は。ここで、自分のログ出力関数でファイル出力すれば良い。
// このサンプルではコンソールに出力する。
printf("%s\n", message);
break;
}
case _CRT_ERROR:
{
// assert等が呼ばれた場合
printf("%s\n", message);
break;
}
default:
{
printf("%s\n", message);
break;
}
}
// FALSEを返すと_CrtSetReportHookで登録したハンドラを呼び出す。
return false;
}
int main() {
// プログラム終了時に自動でメモリリーク箇所を出力する設定
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
// 第一引数には、_CRT_RPTHOOK_INSTALL(ハンドラ追加)か_CRT_RPTHOOK_REMOVE(ハンドラ削除)を指定
// 第二引数には、イベントハンドラを指定する
_CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, myHookFunction);
std::vector<std::vector<int> > hoge, any;
type::list some;
some.push(hoge); // ここでメモリリーク
some.get(some.begin()->next, any); // ここでもメモリリーク
return 0;
}
Detected memory leaks!
Dumping objects ->
{158}
normal block at 0x0026E310, 8 bytes long.
Data: < & > B8 D0 26 00 00 00 00 00
Object dump complete.
と表示されました。
Re: メモリリークを解消したい
解決出来ました。仮想デストラクタを追加することで、メモリリークを検出しなくなりました。
デストラクタの開放漏れだったようですね。
/*---------------------------------------------------------------
listを構成する最小要素
---------------------------------------------------------------*/
class cell_base : public mem_base {
public:
cell_base *next;
cell_base *prev;
cell_base() : next(NULL), prev(NULL), mem_base() {}
virtual ~cell_base() {} // デストラクタ追加
}; // end class cell
/*---------------------------------------------------------------
listのデータ部
---------------------------------------------------------------*/
template <class U> class cell final : public cell_base {
public:
U data;
cell() : cell_base(){}
virtual ~cell(){} // デストラクタ追加
};