ベクトル要素の破棄順序は C++ 標準では定義されていないことを知っています ( std::vector の要素の破棄順序を参照)。チェックしたすべてのコンパイラが最初から最後までこの破棄を行うことを確認しました。動的配列と静的配列は逆の順序でそれを行います。この逆の順序は C++ の世界ではよくあります。
厳密に言うと、「コンテナーメンバーは、たとえばメンバー関数の挿入および消去を使用して、任意の順序で構築および破棄できる」ことを知っており、「これらの変更について何らかのログを保持するコンテナー」には投票しません。現在のベクトル デストラクタの実装を要素の前方破壊から後方破壊に変更することに投票するだけです。そして、このルールを C++ 標準に追加するかもしれません。
で、その理由は?配列からベクトルへの変更は、この方法でより安全になります。
実際の例: ミューテックスのロックとロック解除の順序が非常に重要であることは誰もが知っています。ロック解除を確実にするために、ScopeGuard パターンが使用されます。それから破壊順序が重要です。この例を考えてみましょう。そこで-配列からベクトルに切り替えるとデッドロックが発生します-それらの破棄順序が異なるためです:
class mutex {
public:
void lock() { cout << (void*)this << "->lock()\n"; }
void unlock() { cout << (void*)this << "->unlock()\n"; }
};
class lock {
lock(const mutex&);
public:
lock(mutex& m) : m_(&m) { m_->lock(); }
lock(lock&& o) { m_ = o.m_; o.m_ = 0; }
lock& operator = (lock&& o) {
if (&o != this) {
m_ = o.m_; o.m_ = 0;
}
return *this;
}
~lock() { if (m_) m_->unlock(); }
private:
mutex* m_;
};
mutex m1, m2, m3, m4, m5, m6;
void f1() {
cout << "f1() begin!\n";
lock ll[] = { m1, m2, m3, m4, m5 };
cout <<; "f1() end!\n";
}
void f2() {
cout << "f2() begin!\n";
vector<lock> ll;
ll.reserve(6); // note memory is reserved - no re-assigned expected!!
ll.push_back(m1);
ll.push_back(m2);
ll.push_back(m3);
ll.push_back(m4);
ll.push_back(m5);
cout << "f2() end!\n";
}
int main() {
f1();
f2();
}
OUTPUT - f1() から f2() への破棄順序の変更を確認します
f1() begin!
0x804a854->lock()
0x804a855->lock()
0x804a856->lock()
0x804a857->lock()
0x804a858->lock()
f1() end!
0x804a858->unlock()
0x804a857->unlock()
0x804a856->unlock()
0x804a855->unlock()
0x804a854->unlock()
f2() begin!
0x804a854->lock()
0x804a855->lock()
0x804a856->lock()
0x804a857->lock()
0x804a858->lock()
f2() end!
0x804a854->unlock()
0x804a855->unlock()
0x804a856->unlock()
0x804a857->unlock()
0x804a858->unlock()