Я знаю что порядок уничтожения элементов вектора не определен стандартом 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 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();
}
ВЫВОД - см. изменение порядка уничтожения с 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()