3

次のコードを検討してください。

#include <iostream>
#include <type_traits>

// Abstract base class
template<class Crtp>
class Base
{
    // Lifecycle
    public: // MARKER 1
        Base(const int x) :  _x(x) {}
    protected: // MARKER 2
        ~Base() {}

    // Functions
    public:
        int get() {return _x;}
        Crtp& set(const int x) {_x = x; return static_cast<Crtp&>(*this);}

    // Data members
    protected:
        int _x;
};

// Derived class
class Derived
: public Base<Derived>
{
    // Lifecycle
    public:
        Derived(const int x) : Base<Derived>(x) {}
        ~Derived() {}
};

// Main
int main()
{
    Derived d(5);
    std::cout<<d.set(42).get()<<std::endl;
    return 0;
}

Derivedfromの公開継承が必要で、基本クラスに仮想デストラクタが必要ない場合、何も悪いことが起こらないことを保証するためのコンストラクタ ( ) とデストラクタ ( )Baseの最適なキーワードは何でしょうか?MARKER 1MARKER 2Base

4

3 に答える 3

3

どのようなプログラミング スタイルを使用しても、常に何か悪いことをする可能性があります。最善のガイドライン プラクティスに従っている場合でもです。それはその背後にある物理的なものです(そして、グローバルエントロフィを減らすことが不可能であることに関連しています)

とは言っても、「古典的な OOP」 (方法論) を C++ (言語) と、OOP 継承 (リレーション) を C++ 継承 (集約メカニズム) と、OOP ポリモーフィズム (モデル) を C++ ランタイムおよび静的ポリモーフィズム (ディスパッチメカニズム)。

名前が一致することもありますが、C++ のものは必ずしも OOP のものにサービスを提供する必要はありません。

いくつかの非仮想メソッドを持つベースからのパブリック継承は正常です。また、デストラクタは特別なものではありません。CRTP ベースで delete を呼び出さないでください。

従来の OOP とは異なり、CRTP ベースは派生物ごとに異なる型を持つため、「共通型へのポインター」がないため、「ベースへのポインター」を持つことは無知です。したがって、「delete pbase」を呼び出すリスクは非常に限られています。

「保護された dtor パラダイム」は、ポインターベースのポリモーフィズムを介して管理される (および削除される) オブジェクトの C++ 継承を使用して OOP 継承をプログラミングしている場合にのみ有効です。他のパラダイムに従っている場合、それらのルールを文字通りに扱うべきではありません。

あなたの場合、保護された dtor はBase<Derived>、スタック上に を作成し、Base* で削除を呼び出すことを拒否するだけです。「Dervied」のない Base は存在する意味がBase<Derived>*なく、 a だけを持つことができるのでa を持つことは意味がないため、決してしないことですDerived*。したがって、 public ctor と dtor の両方を持っていても、特に混乱することはありません。

ただし、ctor と dtor の両方を保護するという反対の選択を行うこともできますBase

CRTP の特定の構造により、「危険なユースケース」がなくなるため、すべての古典的な OOP は一種の「無関心な均衡」につながります。

使うか使わないかは別として、特に悪いことは起こりません。オブジェクトを設計どおりに使用する場合はそうではありません。

于 2013-01-10T11:32:28.510 に答える
1

コードが機能している間、コンストラクタではなくデストラクタをとしてマークするのは奇妙だと思います。通常、私の推論は、プログラマーが誤って CRTP ベース オブジェクトを作成するのを防ぎたいということです。もちろん、結果はすべて同じですが、これは標準的なコードとは言えません。protected

コードで防げるのは、ベース ポインターを介した CRTP オブジェクトの誤った削除だけです。つまり、次のような場合です。

Base<Derived>* base = new Derived;
delete base;

しかし、CRTP はそのように使用されることは想定されていないため、これは非常に人為的な状況であり、実際のコードでは発生しません。CRTP ベースは、クライアント コードから完全に隠す必要がある実装の詳細です。

したがって、この状況に対する私のレシピは次のようになります。

  • 保護されたコンストラクターを定義します。
  • デストラクタを定義しないでください。または、CRTP セマンティックで必要な場合は、デストラクタをpublic(および非仮想として) 定義してください。
于 2013-01-10T13:10:07.177 に答える
0

問題はありません。デストラクタは保護されているため、クライアント コードは Base へのポインタを削除できないため、Base のデストラクタが非仮想であっても問題はありません。

于 2013-01-10T11:18:22.250 に答える