親クラスの非仮想デストラクタについて †基底クラスのデストラクタを仮想(virtual)にする理由 †
非仮想な基底クラス class Super { public: Super(){} ~Super(){} // virtualでない }; // サブクラス class Sub : public Super { public: Sub() : m_sub(new int[10]) {} ~Sub() { delete [] m_sub; } private: int* m_sub; }; Super* super = new Sub; // 基底クラスのポインタとしてsubを生成 ... delete super; // Superのデストラクタは仮想ではないため、 // Subのデストラクタは呼ばれない。(コンパイラは // superが実はSubのポインタだということを知るす // べがない) Sub::m_subがメモリリークする! もしSuperのデストラクタが仮想の場合、 Super* super = new Sub; // 基底クラスのポインタとしてsubを生成 // superには仮想関数を処理するための // v-tableという領域(の*)が付加される ... delete super; // Superのデストラクタは仮想なので、v-tableを // 参照し、Subのデストラクタが呼ばれた後に // Superのデストラクタが呼ばれる。したがって、 // メモリリークは発生しない。 仮想デストラクタがあると何が起こるか? †C++のメモリ管理と仮想関数の仕組み (+文字はコンパイラによって自動的に追加されるコード) // クラス定義 class Super { + struct _vtable_t { + void (*destructor)(); + } + static _vtable_t _vtable = { &~Super }; + _vtable_t* _p_vtable;}; public: Super(){} virtual ~Super(){} private: int m_super; }; // サブクラス class Sub : public Super { + static _vtable_t _vtable = { &~Sub }; public: Sub() : m_sub(new int[10]) {} ~Sub() { delete [] m_sub; } private: int* m_sub; }; newとdelete時の動作 Super* super = new Sub; + super = (Super*)malloc(sizeof(Sub)); + super->_p_vtable = &Sub::_vtable; + Sub::Sub(super); // コンストラクタを呼び出す ..... delete super; + super->_p_vtable->destructor(); // Sub::~Sub()が呼ばれる + free((void*)super); // メモリ管理システムにより、malloc // したときのメモリブロックが解放される
オーバヘッドを回避するにはどうすればよいか? †
オーバヘッドを回避する方法 †
結論 †
[ 戻る ]
作成中です |