C++の、特に知らなくても問題ない知識。
virtual関数のデストラクターをもつクラスの、deallocation functionがdeleted定義されていたり、名前が曖昧である場合、エラーとなる。
struct X
{
virtual ~X() { } // OK、グローバルスコープのoperator deleteが発見される
} ;
struct Y
{
virtual ~Y() { } // エラー、operator deleteはdeleted定義されている。
void operator delete( void * ptr ) = delete ;
} ;
これは、クラスYのvirtualなデストラクターを定義した時点でエラーになる。たとえ、クラスYのオブジェクトをnew、deleteしていなくても、ましてや、プログラム中でクラスYのオブジェクトを使っていなかったとしても、関係はない。デストラクタの定義の時点でのエラーである。
これは、デストラクターがvirtual関数である動的な型をもつオブジェクトは、かならず、delete式に渡せるということを保証するための仕様である。
メンバー関数としてのoperator deleteのオーバーロード関数は、staticメンバー関数である。つまり、virtual関数にはできない。ただし、デストラクターがvirtual関数の場合、operator deleteも動的に呼び出される。
struct Base
{
// staticメンバー関数
void * operator new( std::size_t size ) { return ::operator new(size) ; }
void operator delete( void * ptr ) { ::operator delete( ptr ) ;}
} ;
struct Derived : Base
{
// Baseのメンバーを隠す
void * operator new( std::size_t size ) { return ::operator new(size) ; }
void operator delete( void * ptr ) { ::operator delete( ptr ) ; }
} ;
int main()
{
Base * ptr = new Derived() ;
delete ptr ; // Base::operator deleteが呼ばれる
}
なぜならば、呼び出すべきoperator deleteは静的に決定されるからだ。
もし、クラスがvirtual関数のデストラクターを持っていた場合、delete式では、動的な方に応じて、deallocation functionが実行時に選ばれる。
struct Base
{
// staticメンバー関数
void * operator new( std::size_t size ) { return ::operator new(size) ; }
void operator delete( void * ptr ) { ::operator delete( ptr ) ; }
virtual ~Base() { }
} ;
struct Derived : Base
{
// Baseのメンバーを隠す
void * operator new( std::size_t size ) { return ::operator new(size) ; }
void operator delete( void * ptr ) { ::operator delete( ptr ) ; }
// 暗黙のデストラクターはvirtual関数になる
} ;
int main()
{
Base * ptr = new Derived() ;
delete ptr ; // Derived::operator deleteが呼ばれる
}
ブロックスコープ内の宣言は、外側のスコープの名前を隠す。これは、関数にも適用される。
void f(int) { }
int main()
{
void f() ; // ブロックスコープ内の関数の前方宣言、void f(int)を隠す
f( 0 ) ; // エラー、void f(int)は見つからない
}
void f() { }
ブロックスコープでは、関数宣言を行わないほうがいいだろう。もっとも、ブロックスコープで関数宣言ができるということを知っているC++ユーザーの方が少数派かもしれないが。
No comments:
Post a Comment