12

メソッド仮想性の伝播を停止する機能を削除する理由は何ですか?

より明確にしましょう: C++ では、派生クラスに「virtual void foo()」または「void foo()」を記述しても、基底クラスで foo が virtual と宣言されている限り、virtual になります。

これは、派生* ポインターを介した foo() の呼び出しは、プログラマーがこの動作を望んでいない場合でも、仮想テーブル ルックアップになることを意味します (派生 2 関数が foo をオーバーライドする場合)。

仮想性の伝播を停止することがどのように役立つかの例 (私にはかなり露骨に見えます) を挙げましょう。

template <class T>
class Iterator // Here is an iterator interface useful for defining iterators
{              // when implementation details need to be hidden
public:
    virtual T& next() { ... }
    ...
};

template <class T>
class Vector
{
public:
    class VectIterator : public Iterator<T>
    {
    public:
        T& next() { ... }
        ...
    };
    ...
};

上記の例では、Iterator 基本クラスを使用して、より明確でオブジェクト指向の方法で「型消去」の形式を実現できます。(型消去の例については、http://www.artima.com/cppsource/type_erasure.htmlを参照してください。)

それでも、私の例では、インターフェイスを使用せずに実際のオブジェクトにアクセスするために、Vector::VectIterator オブジェクトを直接使用できます (ほとんどの場合これが行われます)。

仮想性が伝播されなかった場合、Vector::VectIterator::next() への呼び出しは、ポインターまたは参照からであっても仮想ではなく、Iterator インターフェイスが存在しないかのように、インライン化して効率的に実行できます。

4

4 に答える 4

10

C++11 では、finalこの目的のために contextual キーワードが追加されました。

class VectIterator : public Iterator<T>
{
public:
    T& next() final { ... }
    ...
};

struct Nope : VecIterator {
    T& next() { ... } // ill-formed
};
于 2012-12-20T12:44:57.703 に答える
0

私の意見では、この伝播の正当な理由の 1 つはvirtualデストラクタです。virtualC++ では、いくつかのメソッドを持つ基本クラスがある場合、デストラクタを定義する必要がありますvirtual。これは、実際には派生クラスを指している基本クラスのポインターがコードに含まれている可能性があり、このポインターを削除しようとするためです (詳細については、この質問を参照してください)。基本クラスでデストラクタを定義することにより、vritual(継承の任意のレベルで)派生クラスを指す基本クラスのすべてのポインタが適切に削除されることを確認できます。

于 2012-12-20T14:43:55.983 に答える
0

その理由は、継承構造の途中で仮想性を削除するのは本当に混乱するからだと思います (複雑さの例を以下に示します)。

ただし、いくつかの仮想呼び出しを削除するマイクロ最適化が懸念される場合は、心配する必要はありません。仮想子メソッドのコードをインライン化し、かつ反復子が参照ではなく値によって渡される限り、優れた最適化コンパイラは、コンパイル時に動的な型を認識し、それにもかかわらず、すべてをインライン化することができます仮想メソッドであること!

ただし、完全を期すために、非仮想化できる言語で次のことを検討してください。

class A
{
public:
    virtual void Foo() { }
};

class B : public A
{
public:
    void Foo() { } // De-virtualize
};

class C: public B
{
public:
    void Foo() { } // not virtual
};

void F1(B* obj)
{
    obj->Foo();
    static_cast<A*>(obj)->Foo();
}

C test_obj;
F1(test_obj);   // Which two methods are called here?

どのメソッドが呼び出されるかについて正確なルールを作成できますが、当然の選択は人によって異なります。メソッドの仮想性を伝播する方がはるかに簡単です。

于 2012-12-20T16:01:25.207 に答える