2

std::vector私のC++クラスの1つは、そのコンテンツに対してカスタムアクションを実行するコンテナーとしても機能できるように派生しています。残念ながら、コンパイラはデストラクタが仮想ではないことについて不平を言います。これは標準ライブラリにあるため、変更できません。

私はすべてを間違ってやっていますか(あなたはSTLから派生してはいけません)、それともコンパイラを幸せに保つために私ができることがありますか?(-Weffc++の使用を停止することからのアパート:)

編集:派生クラスはベクトル操作アルゴリズムに影響を与えませんが、画像のベクトルの「要素の幅/高さ」などの情報を追加するだけです。例として、あなたは考えることができます

class PhotoAlbum: public std::vector<Photo> {
    String title;
    Date from_time, to_time;
    // accessors for title and dates
    void renderCover(Drawable &surface);
};

ここで、フォトアルバムは、主に、いくつかのメタデータ(タイトルと時間)と、アルバムカバーを作成するためにいくつかの写真のサムネイルを表面にレンダリングするなどのアルバム固有の機能を備えた写真のコレクションと考えます。だから私見、フォトアルバムIS-AのコレクションPhoto、それ以上に-そのようなコレクション。

余分な「コレクション」フィールドを持つgetPhotoVector()メソッドを使用することで得られるメリットはわかりません。PhotoAlbum

4

4 に答える 4

16

コンポジションを使ってみませんか?std::vectorカスタムコンテナのメンバーを作成し、そのメンバーに作用するクラスのメンバー関数としてカスタムアクションを実装するだけstd::vectorです。そうすれば、あなたはそれを完全に制御することができます。さらに、継承が必要ない場合は、継承よりも合成を優先する必要があります。

于 2010-08-30T14:49:27.680 に答える
10

非仮想デストラクタを持つパブリック基本クラスを持つことは安全ですが誰かがクラスのインスタンスを で割り当て、newで参照しvector<...>*、ポインタにキャストし直さずにそのポインタを使用して削除した場合の動作は未定義です。あなたのクラスに。そのため、クラスのユーザーは、そうしないことを知っている必要があります。それらを停止する最も確実な方法は、それらに機会を与えないことです。そのため、コンパイラの警告が表示されます。

このような奇妙な条件をユーザーに課すことなくこの問題に対処するには、C++ のパブリック基本クラスの場合、デストラクタをパブリックで仮想にするか、保護して非仮想にすることをお勧めします ( http://www. gotw.ca/publications/mill18.htm、ガイドライン #4)。のデストラクタstd::vectorはどちらでもないため、パブリック基本クラスとして使用しないでください。

ベクトルに対していくつかの追加操作を定義することだけが必要な場合は、それが C++ の自由関数の目的です。.とにかく、メンバー呼び出し構文の何がそんなに優れているのでしょうか? のほとんどは<algorithm>、ベクターおよびその他のコンテナーに対する追加の操作で構成されています。

たとえば、セマンティクスが変更されたインターフェイス全体を提供する「最大サイズ制限のあるベクトル」を作成する場合、vector実際には、継承と仮想呼び出しが標準である言語と比較して、C++ は少し不便です。 . 最も簡単なのは、プライベート継承を使用してvectorから、変更したくないメンバー関数について、次のようにしてクラスに持ち込むことですusing

#include <vector>
#include <iostream>
#include <stdexcept>

class myvec : private std::vector<int> {
    size_t max_size;
  public:
    myvec(size_t m) : max_size(m) {}
    // ... other constructors

    void push_back(int i) {
        check(size()+1);
        std::vector<int>::push_back(i);
    }
    // ... other modified functions

    using std::vector<int>::operator[];
    // ... other unmodified functions

  private:
    void check(size_t newsize) {
        if (newsize > max_size) throw std::runtime_error("limit exceeded");
    }
};

int main() {
    myvec m(1);
    m.push_back(3);
    std::cout << m[0] << "\n";
    m.push_back(3); // throws an exception
}

ただし、それでも注意が必要です。vectorC++ 標準では、 のどの関数が互いにどのような方法で呼び出すかは保証されていません。これらの呼び出しが発生する場所では、vector基本クラスが のオーバーロードを呼び出す方法がないmyvecため、変更した関数は単に適用されません。これは非仮想関数です。resize()サイズを変更するすべての関数をオーバーロードしmyvec、それらすべてをcheck(直接または相互に呼び出して) 呼び出す必要があります。

標準の制限から、いくつかのことが不可能であると推測できます。たとえば、operator[]ベクトルのサイズを変更できないため、私の例では基本クラスの実装を安全に使用でき、関数をオーバーロードするだけで済みます。サイズが変わる可能性があります。しかし、標準は、考えられるすべての派生クラスに対して、必ずしもそのような保証を提供するわけではありません。

つまりstd::vector、基本クラスとして設計されていないため、適切に動作する基本クラスではない可能性があります。

もちろん、プライベート継承を使用するmyvec場合、ベクトルを必要とする関数に渡すことはできません。しかし、それはベクトルではないためです。そのpush_back関数はベクトルと同じセマンティクスを持っていないため、LSP に関して危険な根拠に基づいていますが、より重要なことはvector、オーバーロードを無視する関数への非仮想呼び出しです。標準ライブラリが期待する方法で処理を行う場合は問題ありません。多くのテンプレートを使用し、コレクションではなくイテレータを渡します。仮想関数呼び出しが必要な場合は問題vectorありません。仮想デストラクタがないという事実は別として、仮想関数がないためです。

標準コンテナを使用した動的ポリモーフィズムが実際に必要な場合 (つまり、実行したい場合 vector<int> *ptr = new myvec(1);) は、「してはならない」領域に入っていることになります。標準ライブラリは本当に役に立ちません。

于 2010-08-30T19:36:18.793 に答える
1

私はすべてを間違ってやっていますか

おそらく。

vectorあなたはあなたが特別な機能を提供するために派生したと言います。これが通常行われる方法は、組み込みのアルゴリズムを独自のファンクターと組み合わせて使用​​して、この特別な機能を提供することです。

から派生する代わりにこれを行うと、vector複数の利点があります。

1)スコープがに忍び寄るのを防ぐのに役立ちますvectorvectorの仕事は、オブジェクトのコレクションを維持することです。それらのオブジェクトに対してアルゴリズム機能を実行しないでください。vector特殊関数から派生して追加するvectorことで、別の仕事を与えられたため、より複雑になります。

2)それは物事を行う「STLの方法」とより一致しています。これにより、STLを知っているが、STLコレクションとは異なる動作をする特別なクラスを知らない可能性がある人々が将来的に保守しやすくなります。

3)それはより拡張可能です。適切に記述されたファンクターは、どのようなコレクションに作用するかを気にしません。何らかの理由で、のlist代わりにを使用したい場合、ファンクターを使用している場合は、新しい特別な派生クラスvectorを再実装するよりも、リファクタリングする方がはるかに簡単です。list

4)全体的にシンプルな設計であるため、欠陥の影響を受けにくく、保守が容易です。

于 2010-08-30T14:56:39.287 に答える
1

たぶん、継承の代わりに構成を使用しますか?なぜベクトルを拡張しているのかはよくわからないので、これがオプションかどうかはわかりません。

于 2010-08-30T14:50:41.690 に答える