前提:C++の仮想関数は派生もとの関数の宣言にvirtualがあれば、派生先の同じ関数(名前、戻り値、引数)はvirtualがなくてもオーバーライドされます。C++/CLIでは派生先でvirtualを付けない限りオーバーライドされることはありません。
override, new, sealedは仮想関数の扱いを拡張します。virtualとともに使います。関数宣言の後ろにおいて使います。
using namespace System; interface struct I { void func(); }; ref struct A { virtual void func() sealed { Console::WriteLine("A"); } }; ref struct B : A { virtual void func() new { Console::WriteLine("B"); } }; ref struct C : B, I { virtual void func() override { Console::WriteLine("C"); } }; ref struct D : C { virtual void func() new = I::func { Console::WriteLine("D"); } }; ref struct F : C { virtual void func2() sealed = I::func { Console::WriteLine("F"); } }; ref struct G : F { virtual void func() override { Console::WriteLine("G"); } }; void testA(A^ a) { a->func(); } void testB(B^ b) { b->func(); } void testC(C^ c) { c->func(); } void testD(D^ d) { d->func(); } void testF(F^ f) { f->func(); } void testI(I^ i) { i->func(); } int main(array<System::String ^> ^args) { C^ c = gcnew C; c->func(); testA(c); testB(c); testI(c); D^ d = gcnew D; d->func(); testA(d); testB(d); testI(d); F^ f = gcnew F; f->func(); testA(f); testB(f); testC(f); testI(f); F^ g = gcnew G; g->func(); testA(g); testB(g); testC(g); testF(g); testI(g); return 0; }
その他overrideキーワードでオーバーライドした関数を派生クラスが名前を指定して再びオーバーライドすることはできません。
たとえばFormの派生クラスでWndProc(Message% m)をオーバーライドする場合、以下のように書くことはできません。
virtual void WndProc(Message% m) override = Form::WndProc;
以下のどちらかの書き方でなくてはなりません。
virtual void WndProc(Message% m) override = Control::WndProc;
virtual void WndProc(Message% m) override ;
.NET Frameworkではバイナリでクラスを参照できるため、仮想関数の名前の衝突が予測され、また仮想関数が消えたりすることもありえます。あるクラスが 2つインターフェース,I1,I2を継承していたとして、I1::GetText()をオーバーライドしたつもりでいたが、いつのまにかI1から GetText()が消え、I2にGetText()ができていた、と言う場合も考えられます。こうなった場合プログラムの意味はまったく変わってしまうにもかかわらず、コンパイラは何も教えてくれません。
このようなDLL-HELLを防ぐための仕組みです。上記の場合は名前つきでオーバーライドしていればコンパイラがエラーをはいてくれます。
override関連は非常に複雑で理解しにくいです。上の話もうそがあるかもしれません。少なくとも使う側で注意すべきなのは・・・
だと思います。