メモリリークを出力させてみましょう(2)
前回は、VC++で提供されているメモリリークのチェックを用いました。
ただ、このメモリリークのチェックは、Qtとは、基本的に、整合性がとれないために
正しく、出力でされるとは限らないことは、前回、少し、記述しました。
では、他にやり方はないでしょうか。
Windowsで実施する場合、WinDbgでメモリリークを見つける方法は、無料で行うための
手段として、よく知られるところだと思います。
ただ、VC++に慣れてしまった人間としては、WinDbgのコマンドからスクリプトまで
(コマンドもスクリプトも同じようなものですが)を行うのに、少々、時代に逆行しているようで
なんとも組したくないものでもあります。
では、WinDbg以外で何か良い方法はないものでしょうか。
実は、参考記事のリンク先のメモリリーク検出ライブラリが、それです。
私が使った感じでは、特別、不適切な出力はありませんでしたので、少し、紹介したいと思います。
(もちろん、バグはあると思いますが・・・。)
前置きが長くなりましたが、早速、試してみましょう。
まず、関連記事のWEBサイトからデモ用ソースコードをダウンロードしましょう。
先の参考記事の前者(codeproject)が、C++用です。後者(codeguru)が、C用です。
それぞれ、異なる作者ですので、それぞれやり方が違います。しかし、いずれも、ソースコードの変更は、ほぼありません。
(環境を取り込むための#includeぐらいです)
また、C++用(codeproject)は、DLL版をダウンロードします。
(VC++2008+Qtの環境では、スタティックリンクでは、エラーが多発します。)
ダウンロードした traceallocations.cpp の371行目を以下のように変更します。
(恐らくバグだと思います)
	
371
372
  | //    if (index <= MAXSTACK) {
    if (index < MAXSTACK) {
  | 
 
	 
VC++2008で、サンプルをコンパイルの上、実行してみてください。
以下のようなメッセージが出力されればOKです。
(ソースコードへのファイルパスは、それぞれの環境によって異なります。)
Leak of 4 bytes detected (452 bytes with headers and no mans land)
     ...\memleakfinder4dll\tracealloc.cpp(8) : operator new
     ...\memleakfinder4dll\main.cpp(12) : generate_memoryleak
     ...\memleakfinder4dll\main.cpp(19) : main
     ...\crt_bld\self_x86\crt\src\crt0.c(266) : __tmainCRTStartup
     ...\crt_bld\self_x86\crt\src\crt0.c(182) : mainCRTStartup
     0x0012B7A4 : _BaseProcessStart@4
では、実際に前回の記事で扱ったソースコードを確認してみましょう。
Qtアプリ側のVC++プロジェクトを変更してしまうと、Qtプロジェクトとの整合性をなくしてしまいますので
ここでは、ソースコードを以下のように変更して、対応します。
Qtアプリ側へ、tracealloc.cpp を取り込み、MemLeakDll.libをLIBリンクします。
このとき、先のDLLのプロジェクトから、以下のファイルをQtアプリ側のVC++プロジェクトディレクトリの配下へコピーします。
	- tracealloc.cpp
	
 
	- MemLeakFindDll.h
	
 
	- MemLeakDll.lib
	
 
	- MemLeakDll.dll
	
 
	
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  | #if !defined(QT_NO_DEBUG) && defined(WIN32)
#include <crtdbg.h>
#endif
 
 
 
#include "QApplication"
#include "QPushButton"
 
int main(int argc, char *argv[])
{
#if !defined(QT_NO_DEBUG) && defined(WIN32)
    _CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF );
#endif
    QApplication app(argc, argv);
    QPushButton hello("Hello world");
    hello.resize(100, 30);
 
	//	メモリリーク
	int *pint=new int[10];
 
    hello.show();
    return app.exec();
}
  | 
 
	 
 
	
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  | #if !defined(QT_NO_DEBUG) && defined(WIN32)
#define DETECT_LEAKS
#include "tracealloc.cpp"
#pragma comment(lib, "MemLeakDll.lib")
#endif
 
#include "QApplication"
#include "QPushButton"
 
int main(int argc, char *argv[])
{
 
 
 
    QApplication app(argc, argv);
    QPushButton hello("Hello world");
    hello.resize(100, 30);
 
	//	メモリリーク
	int *pint=new int[10];
 
    hello.show();
    return app.exec();
}
  | 
 
	 
 
準備ができたら、コンパイルして実行してみてください。
先のデモと同様、出力エリアに"Leak of 40 bytes detected "のメッセージが出力されたことと思います。
同じように、先の記事の以下の3つのファイルからなるサンプルでも同様に確認してみましょう。
	- MyWidget.h
		-- メイン画面(ヘッダ)で、終了するボタンを一つだけ持ちます。
	 
	- MyWidget.cpp
		-- メイン画面(ソース)で、終了するボタンを一つだけ持ちます。
	 
	- sample.cpp
		-- メイン画面を表示して、終了するだけです。
	 
手を加えるのは、先のサンプルと同様 sample.cpp のみです。
[MyWidget.h]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  | #ifndef _MYWIDGET_H_
#define _MYWIDGET_H_
 
#include "QApplication"
#include "QPushButton"
#include "QVBoxLayout"
 
////////////////////////////////////////
//	メモリーリーク検証のためのデータクラス
class LocalData
{
public:
	LocalData();
	int m_ndata;
};
 
////////////////////////////////////////
//	メモリーリーク検証のための画面クラス
class MyWidget : public QWidget
{
public:
	MyWidget( QWidget *parent=0 ) ;
 
 
	QPushButton *m_pquit;	//	ボタンをnewする
	QVBoxLayout *m_playout;	//	レイアウトをnewする
	QPushButton *m_pquit2;	//	ボタンをnewする
 
	LocalData *m_plocaldata;	//	ローカルデータをnewする
};
#endif
  | 
 
 
10-15行目は、メモリリーク検証用にQtに依存しないクラスを定義しています。
[MyWidget.cpp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  | #include "MyWidget.h"
 
////////////////////////////////////////
//	メモリーリーク検証のためのデータクラス
LocalData::LocalData ()
{
	m_ndata=99;
}
 
 
////////////////////////////////////////
//	メモリーリーク検証のための画面クラス
MyWidget::MyWidget( QWidget *parent) : QWidget( parent, 0 )
{
	setMinimumSize( 200, 100 );
	setMaximumSize( 400, 400 );
 
	//	ボタンをnewする
	m_pquit   = new QPushButton( "Quit", this );
	//	レイアウトをnewする
	m_playout = new QVBoxLayout;
	m_playout->addWidget(m_pquit);
	setLayout(m_playout);
 
	//	関連のないボタンをnewする
	//	*****このオブジェクトは、メモリーリークと判断される
	m_pquit2   = new QPushButton( "Quit2");
 
	//	ローカルデータをnewする
	//	*****このオブジェクトは、メモリーリークと判断される
	m_plocaldata = new LocalData;
	Q_ASSERT(m_plocaldata->m_ndata==99);
 
	connect( m_pquit, SIGNAL(clicked()), qApp, SLOT(quit()) );
	setWindowTitle(QString ("test dialog"));
}
  | 
 
 
19行目は、実際にこの画面に貼り付ける"Quit"ボタンを作成(new)しています。
28行目は、この画面に貼り付けない"Quit2"ボタンを作成(new)しています。
32行目は、Qtに依存しませんが、この画面のメンバーとして個別のクラスを生成(new)しています。
[sample.cpp]
	
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  | #if !defined(QT_NO_DEBUG) && defined(WIN32)
#include <crtdbg.h>
#endif
 
 
 
#include "QApplication"
#include "QPushButton"
#include "MyWidget.h"
 
int main(int argc, char *argv[])
{
#if !defined(QT_NO_DEBUG) && defined(WIN32)
    _CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF );
#endif
	QApplication app(argc, argv);
	MyWidget hello;
 
	hello.show();
	return app.exec();
}
  | 
 
	 
 
	
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  | #if !defined(QT_NO_DEBUG) && defined(WIN32)
#define DETECT_LEAKS
#include "tracealloc.cpp"
#pragma comment(lib, "MemLeakDll.lib")
#endif
 
#include "QApplication"
#include "QPushButton"
#include "MyWidget.h"
 
int main(int argc, char *argv[])
{
 
 
 
	QApplication app(argc, argv);
	MyWidget hello;
 
	hello.show();
	return app.exec();
}
  | 
 
	 
 
17,19行目は、画面を表示しているだけです。
準備ができたら、コンパイルして実行してみてください。
先のデモと同様、出力エリアに"Leak of 40 bytes detected "のメッセージが出力されたことと思います。
また、
[MyWidget.cpp]の26-33行目までをコメントアウトして、同様にデバッグ実行してみると結果は、今度はメモリリークが何も
出力されなくなります。
上々のできだと思います。先の記事でも問題としていましたアイコンのメニューやツールバーでの表示でも
同じようにうまくいきます。(サンプルのdebugmemory3を参照してください)
このサンプルで、前回の記事の方法では、メモリーリークが検出されていましたが、今回の方法では、正しくエラーなしとなります。
現在でも、いろんな形でこのメモリリーク検出ライブラリを使っていますが、正しく動作しているように思います。
ただ、このライブラリで検出できるのは、new,deleteを使ってのメモリ管理したできません。
実際にCのmalloc,freeなどを使った検出を行うには、参考記事(codeguru)を使って行うことができます。ただ、このライブラリは、ファイルに出力しますので、
デバッグ情報の出力エリアへ、先に試したライブラリを元に変更を加えて利用すれば、ほぼ、MFCの環境と同じくらいのメモリリーク検出が行えるようになると思います。
※C側は、やり方が少し異なります。malloc,freeなどを使うところで、ヘッダをincludeしてあげる必要があります。
※CPP側は、一度、取り込んでおけば、同一プロジェクト内では、全て同じように検出してくれます。
いかがだったでしょうか。これで、デバッグも、無料の環境が、ほぼできましたので、Qtアプリを作成できますね。

もっと、Qt関連について詳しく知りたい方は、以下の本なども良いと思います。
Qtに関する日本語の本が少ないですね。「入門書」は、さすがに、このページを読まれるくらいの方は不要だと思います。
やっぱり、本+ネット+試してみる!!の3本柱でやっていく以外にないように思います。
 
		
  
コメントをどうぞ