メモリリークを解消したい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
馬場自由
記事: 15
登録日時: 11年前

メモリリークを解消したい

#1

投稿記事 by 馬場自由 » 10年前

様々な型を一つのリストに格納できる双方向リストを作りたいと思い、下記のコードを書きました。現在、push()とget()でメモリリークが発生しており、自分では原因がわからず困っています。よろしくお願いします。

コード:

  /*=================================================================
  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

検証コード:

コード:

std::vector<std::vector<CHoge> > hoge, any;
type::list some;
some.push(hoge); // ここでメモリリーク
some.get(some.begin()->next, any); // ここでもメモリリーク

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: メモリリークを解消したい

#2

投稿記事 by softya(ソフト屋) » 10年前

出来れば、コンパイルが通る1つのソースコードとして提供して貰いたいのですが、無理でしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
馬場自由
記事: 15
登録日時: 11年前

Re: メモリリークを解消したい

#3

投稿記事 by 馬場自由 » 10年前

全文はこんな感じです。中身は、main.cc, Type.cc, Type.hppです。
添付ファイル
main.zip
十分注意してますが、念のためウイルスチェックを各自お願いします
(3.84 KiB) ダウンロード数: 120 回

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: メモリリークを解消したい

#4

投稿記事 by softya(ソフト屋) » 10年前

C++11だと言うのは分かるのですが、cygwinなど当方の環境で今のところコンパイルできていません。
VC++2012は起動できないので未調査です。
環境が出来るまでお待ちください。 あと調べてみないと分かりませんが、C++11に詳しくないので答えられないかもしれません。

ちなみに、このコードの環境を教えて下さい。LinuxとかUnixだと思うんですが。

【追記】
※ VC++2012のコマンドラインではコンパイル出来ました。ただし、CRTDBGのレポートが出ませんでした。調査中。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: メモリリークを解消したい

#5

投稿記事 by h2so5 » 10年前

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: メモリリークを解消したい

#6

投稿記事 by softya(ソフト屋) » 10年前

こちらも、VC++2012コマンドラインで
cl main.cc Type.cc /EHsc /D_DEBUG /DEBUG /MDd /Zi /Od
でコンパイルリンクしましたが、実行にリークは検出されていません。

【追記】 環境が明確で無い為、追試験が難しい状況です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
a5ua
記事: 199
登録日時: 13年前

Re: メモリリークを解消したい

#7

投稿記事 by a5ua » 10年前

コード:

    /*---------------------------------------------------------------
    後端に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;
    }
push()で、cell<U>としてnewしたポインタを、
pop()では、cell_baseとしてdeleteしているので、
少なくともcell_baseクラスには仮想デストラクタが必要だと思います。

アバター
馬場自由
記事: 15
登録日時: 11年前

Re: メモリリークを解消したい

#8

投稿記事 by 馬場自由 » 10年前

>softyaさん
環境はVS2012で、OSはWin7です。ビルドはプロジェクトを作成した初期状態のままで行なっています。

>a5uaさん
確かにそうですね、ありがとうございます。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: メモリリークを解消したい

#9

投稿記事 by softya(ソフト屋) » 10年前

すいません。私の所ではプロジェクトが作れない → VS2012のIDEが起動できないのでコマンドラインでしか確認できません。
なにかオプションが違うだけだとは思うんですが。
試しにコンソールでビルド実行してみてもらえませんか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
馬場自由
記事: 15
登録日時: 11年前

Re: メモリリークを解消したい

#10

投稿記事 by 馬場自由 » 10年前

cl main.cc Type.cc /EHsc /D_DEBUG /DEBUG /MDd /Zi /Od を実行しましたが、特に何も表示されませんでした。
デバッグ機能がコンソールに表示されてないのかな?と思い、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.

と表示されました。

アバター
馬場自由
記事: 15
登録日時: 11年前

Re: メモリリークを解消したい

#11

投稿記事 by 馬場自由 » 10年前

解決出来ました。仮想デストラクタを追加することで、メモリリークを検出しなくなりました。

コード:

    /*---------------------------------------------------------------
    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(){} // デストラクタ追加
    };
デストラクタの開放漏れだったようですね。

閉鎖

“C言語何でも質問掲示板” へ戻る