• ベストアンサー

newされたポインタとdeleteされるポインタが…

いま私は、std::mapを使って、newされたポインタ(メモリの先頭アドレス)をkeyとして記録し、newされた場所のファイルや行番号、サイズ、何回目のnewか、などなどをvalueとして構造体にまとめて保存してデバッグなどに役立て、deleteされると同時にこの情報を破棄し、最後にすべて破棄されているかどうかを調べる(メモリリークのチェック)という機構を作っています。 ですが、keyとなっているメモリのアドレスは、多重継承や仮想継承とキャストの組み合わせ次第では、newされた時点のものとdeleteされる時点のもので異なっている場合があるため、情報を破棄することができず、行き詰ってしまいました。(無論、delete以外にも情報を引き出す際に影響が…) deleteするポインタから、そのポインタがnewで作成されたときのメモリの先頭アドレスを求める方法はないでしょうか? 以下に問題の部分だけ抽出したようなコードを挙げます。 ---------------- #include <stdio.h> class CLS1{ public: int x,y,z; virtual ~CLS1(){} }; class CLS2{ public: int x,y; virtual ~CLS2(){} }; class CLS3:public CLS1, public CLS2{ public: int x; virtual ~CLS3(){} }; void print(void* p){printf("%p\n",p);} template<typename T> T* create(T* p){ // ここで、たとえば 00854880 と記録されたとして… print(p); return p; } template<typename T> void releace(T* p){ // ここでも 00854880 と記録されてほしいが、 // 00854890 と記録されてしまう。 // この場所で 00854880 と同じインスタンスであることを確かめる方法はないでしょうか? print(p); delete p; } int main(){ CLS2* x = create(new CLS3); // 多重継承しているため、 00854890 と記録される。 print(x); release(x); return 0; }

質問者が選んだベストアンサー

  • ベストアンサー
  • colder
  • ベストアンサー率43% (30/69)
回答No.4

> あぁ、なるほど、int型とか仮想関数を持たないクラスのような非ポリモーフィックな型だったりするとdynamic_castが使えなくてコンパイルエラーを起こすということですか… > ほかにだめな場合はありますか? > また、これらを何らかの方法でふるいにかけることってできますかね? boostを使ってもよいのなら、 #include <boost/type_traits.hpp> template <class T, bool = boost::is_polymorphic<T>::value> struct C { static void *foo(T* p) { return static_cast<void*>(p); } }; template <class T> struct C<T,true> { static void *foo(T* p) { return dynamic_cast<void*>(p); } }; template<typename T> void releace(T* p){ print(C<T>::foo(p)); delete p; }

SHOO-3
質問者

お礼

boost使うなら #include <boost/type_traits/is_polymorphic.hpp> #include <boost/utility/enable_if.hpp> template<typename T> void* getNewMemStartPos(T* x, typename boost::enable_if< boost::is_polymorphic<T> >::type* =0){ return dynamic_cast<void*>(x); } template<typename T> void* getNewMemStartPos(T* x, typename boost::disable_if< boost::is_polymorphic<T> >::type* =0){ return static_cast<void*>(x); } template<typename T> void release(T* p){ print(getNewMemStartPos(p)); delete p; } とか考えましたが… boost使わない方法はない…かな、やっぱ。 仮想関数を持たないクラスを多重継承したようなクラスには残念ながら対応できませんが、しょうがないですね。 当初の質問内容からも若干方向がずれてきましたので、とりあえずこれで締め切らせていただきます。 回答者の皆さんには、厚くお礼申し上げます。

その他の回答 (3)

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.3

例に挙げられているように、多相的クラス(仮想関数を持つクラス)しか扱わないのであれば、#2の方が書かれているように、dynamic_cast<void*>を使った方が簡単です。

SHOO-3
質問者

補足

あぁ、なるほど、int型とか仮想関数を持たないクラスのような非ポリモーフィックな型だったりするとdynamic_castが使えなくてコンパイルエラーを起こすということですか… ほかにだめな場合はありますか? また、これらを何らかの方法でふるいにかけることってできますかね?

  • colder
  • ベストアンサー率43% (30/69)
回答No.2

newした先頭アドレスを知りたいだけなら dynamic_cast<void*>()で可能です。

SHOO-3
質問者

お礼

なるほど、dynamic_castにそんな使い方があったんですね。 自分の求めていたのはまさにこの方法だと思います。 貴重なご意見をお聞かせいただけたこと、うれしく思います。 本当にありがとうございました。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.1

new演算子とdelete演算子をそれぞれ多重定義し、その内部でstd::mapへの登録と削除を行うようにしましょう。 なお、std::map自体も、デフォルトでは内部的にnew/deleteを使うことになるので、アロケータを自作するなどして対応した方がよいと思います。

SHOO-3
質問者

お礼

こういうことでしょうか? void* operator new(size_t sz)throw(){ void* p = malloc(sz); print(p); return p; } void operator delete(void* p)throw(){ print(p); free(p); } (内部リンゲージにするためにinlineをつけたほうがいいのかも?) グローバルな new や delete は(アロケータを自作と指摘されているように、自分で書いていないコードとの関係もあり、)なるべくなら弄りたくないので、#2さんの意見のほうが自分にはいい方法かもしれません。 #2さんのに加え、後は、非ポリモーフィックな場合を想定した何かしらの方法が必要といったところですか… ともあれ、回答ありがとうございました。

関連するQ&A