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