非仮想デストラクタを持つパブリック基本クラスを持つことは安全ですが、誰かがクラスのインスタンスを で割り当て、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
}
ただし、それでも注意が必要です。vector
C++ 標準では、 のどの関数が互いにどのような方法で呼び出すかは保証されていません。これらの呼び出しが発生する場所では、vector
基本クラスが のオーバーロードを呼び出す方法がないmyvec
ため、変更した関数は単に適用されません。これは非仮想関数です。resize()
サイズを変更するすべての関数をオーバーロードしmyvec
、それらすべてをcheck
(直接または相互に呼び出して) 呼び出す必要があります。
標準の制限から、いくつかのことが不可能であると推測できます。たとえば、operator[]
ベクトルのサイズを変更できないため、私の例では基本クラスの実装を安全に使用でき、関数をオーバーロードするだけで済みます。サイズが変わる可能性があります。しかし、標準は、考えられるすべての派生クラスに対して、必ずしもそのような保証を提供するわけではありません。
つまりstd::vector
、基本クラスとして設計されていないため、適切に動作する基本クラスではない可能性があります。
もちろん、プライベート継承を使用するmyvec
場合、ベクトルを必要とする関数に渡すことはできません。しかし、それはベクトルではないためです。そのpush_back
関数はベクトルと同じセマンティクスを持っていないため、LSP に関して危険な根拠に基づいていますが、より重要なことはvector
、オーバーロードを無視する関数への非仮想呼び出しです。標準ライブラリが期待する方法で処理を行う場合は問題ありません。多くのテンプレートを使用し、コレクションではなくイテレータを渡します。仮想関数呼び出しが必要な場合は問題vector
ありません。仮想デストラクタがないという事実は別として、仮想関数がないためです。
標準コンテナを使用した動的ポリモーフィズムが実際に必要な場合 (つまり、実行したい場合
vector<int> *ptr = new myvec(1);
) は、「してはならない」領域に入っていることになります。標準ライブラリは本当に役に立ちません。