ユーザ用ツール

サイト用ツール




サイドバー

C++/CLI

CLR

実用編

その他

cli:override_new_sealed

override, new, sealed

前提:C++の仮想関数は派生もとの関数の宣言にvirtualがあれば、派生先の同じ関数(名前、戻り値、引数)はvirtualがなくてもオーバーライドされます。C++/CLIでは派生先でvirtualを付けない限りオーバーライドされることはありません。

override, new, sealedは仮想関数の扱いを拡張します。virtualとともに使います。関数宣言の後ろにおいて使います。

  1. (なにもなし=virtualのみ)
    • 関数が大元の仮想関数か、派生元のすべてのインターフェース仮想関数をオーバーライドした関数であることを示します。
  2. override
    • 関数が派生元の仮想関数をオーバーライドすることを宣言します。
  3. new
    • 関数が派生元の仮想関数をオーバーライドしないで、大元の仮想関数であることを宣言します。
  4. sealed
    • 関数が大元の仮想関数か派生元のすべてのインターフェース仮想関数を継承した関数で、派生クラスでオーバーライドできないことを宣言します。
  5. 名前つきオーバーライド
    • 関数が特定の派生元のインターフェース仮想関数をオーバーライドすることを宣言します。
  6. override 名前つきオーバーライド
    • 関数が名前の違う仮想関数をオーバーライドすることを宣言します。
  7. override sealed
    • 関数が仮想関数をオーバーライドし、派生クラスでオーバーライドできないことを宣言します。
  8. new 名前つきオーバーライド
    • 関数が特定の派生元の仮想関数のみオーバーライドし、大元の仮想関数であることを宣言します。
  9. sealed 名前つきオーバーライド
    • 新しい名前のこの関数が仮想関数のみオーバーライドし、派生クラスでオーバーライドできないことを宣言します。
  10. virtualなしのnew
    • 派生元の仮想関数を無視して普通の関数を宣言します。
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関連は非常に複雑で理解しにくいです。上の話もうそがあるかもしれません。少なくとも使う側で注意すべきなのは・・・

  1. インターフェースの仮想関数をオーバーライドするときは、overrideをつけられない(virtualのみ)。そして名前つきでオーバーライドしたほうがよい。
  2. インターフェースでない仮想関数をオーバーライドするときはoverrideをつけないといけない。名前つきにする必要なない(?)。

だと思います。




/var/www/html/virtual/cppcli/data/pages/cli/override_new_sealed.txt · 最終更新: 2013/12/23 09:08 (外部編集)