純粋な仮想関数の存在&仮想仕様の存在、およびそのconstの存在

5836 ワード

次のコードについて考察します.
class AbstractBase

{

public:

    virtual IBase() = 0;

    virtual Interface() const = 0;

    virtual const char*

        Member() const {return _member;}

    protected:

        char *_member;

}

このコードには問題があります.このクラスは抽象的ですが、初期化には構造関数が必要です.member. このAbstractBaseの設計者がderived classごとに提供しようとしていると思うかもしれません.memberの初期値しかし、もしそうであれば、derived classの唯一の要件は、AbstractBaseが一意のパラメータprotected constructorを提供する必要があることです.
AbstractBase::

AbstractBase(char *member_value = 0)

    :_member(member_value){}

一般に、classのdataメンバーは初期化すべきであり、constructorまたはclassメンバーfunctionsで初期値を指定するだけである.その他のいかなる操作もパッケージの性質を破壊し、classのメンテナンスと修正をさらに困難にする.もちろん、設計者の間違いはexplicit constructorを提供していないことではなく、抽象的なbase classでdata membersを宣言すべきではないということもあります.インタフェースの思想は間違いないが、共有データをbase classに提出するのも名実ともに順調だ.
C++初心者(例えば私)は、pure virtual functionを定義して呼び出すことができることに驚くことが多い.しかし、それらは静的に呼び出すことしかできず、仮想メカニズムを介して呼び出すことはできない.たとえば、次のコードは正当です.
inline void 

AbstractBase::Interface() const

{

    //  Interface() const

} 



inline void

ConcereteDerived::interface() const

{

    // 

    AbstractBase::Interface();

}

具体的にそうするかどうかはclassの設計者にかかっています.唯一の例外はpure virtual destructorです.classの設計者は必ずそれを定義しなければならない.各derived class destructorはコンパイラによって拡張されるため、virtual base ckassおよび前層base classのdestructorを静的に呼び出す.そのため、1つでも少ないとリンクが失敗する.
PS.私が使っているVC++12.0コンパイラは、error LNK 209:解析できない外部シンボル「public:virtual void__thiscallとfatal error LNK 1120:1解析できない外部コマンド;
gcc 4.8.1にエラーが表示されたのは、undefined reference to`IBase::Interface()const'です.Clangやiccを使っている方がこのコードをコンパイルして成功するかどうか楽しみです...しかし、セグメントコードLippmanは合法だと言っています.私は一応信じましょう.
pure virtual destructorの呼び出し操作は、コンパイラがderived classの額destructorを拡張するときに抑えるべきではないかと思うかもしれません.いいえ、classデザイナーは本当にpure virtual destructorを定義しているかもしれません.このような設計はC++言語の保証を前提としています.統合システムの各class objectのdestructorが呼び出されるので、コンパイラはこの操作を抑えることはできません.
コンパイラはpure virtual destructorの関数定義を合成すると思いますか?残念なことに、コンパイラは実行ファイルに対して分離コンパイルモデルを採用しているので、開発環境はデバイスを提供することができ、リンク時にpure virtual destructorが存在しないことの実体を探し出して、それからコンパイラを再アクティブにして、特殊な命令を与えて、現在コンパイラが本当にこのようにするかどうかまだ分かりません.
仮想規格の存在AbstractBase::Member()をvirtual functionとして設計すると、関数定義がタイプに関係しないため、後続のderived classに書き換えられないという悪い選択になります.また、non-virtual関数エンティティがinline関数であるため、頻繁に呼び出されると効率的な報いは軽くない.
コンパイラは、この関数が実際にclass階層に1つのエンティティしか存在しないことを分析できますか?可能であれば、呼び出し操作を静的呼び出し操作に変換して、その呼び出し操作のinline expansionを許可できますか?もしclass階層体系が続々と新しいclassesに追加されたら、この関数を持つ新しいエンティティはどうなりますか?そう、新しいclassは最適化を破壊します!この関数は再コンパイルされる(または2番目のエンティティが生成され、コンパイラはそれが呼び出されることを決定する).しかし、このような依存性を掘り出すには、何らかの形式のpersistent program databaseやlibray managerが必要になる可能性がある.
したがって、すべてのメンバー関数をvirtual functionと宣言し、コンパイラの最適化操作で不要なvirtual invocationを除去するのは良い考えではない.仮想仕様におけるconstの存在
virtual functionがconstを必要とするかどうかを決めるのは些細なことのようですが、本当に真剣に考えているときは、決めにくいです.このことは、subclassエンティティが無限に使用される可能性があると仮定し、関数をconstと宣言しないことを意味し、const referenceまたはconst pointerを取得できないことを意味する.もっと気持ち悪いのは、constとして関数を宣言してから、実際にderived instanceがdata memberを変更しなければならないことに気づいたことです.提案はvirtual functionにconstを追加しないことです
結論:頼りになる類は頭痛を避ける保証である.
class AbstractBase

{

public:

    virtual ~AbstractBase() = default;

    virtual void  Interface() = 0;

    const   char* Member() const {return _member;}    // ?

protected:

    Abstractbase(char *member = 0):_member(){}        //  constructor

    char *_member;

};