Поиск ошибки в векторе ctor из упражнения в книге TC ++ PL

Я работал над всеми упражнениями из книги Бьярна Страуструпа «Язык программирования C ++».

Я пришел к одному конкретному упражнению (E.8.4) это поставило меня в тупик.
Это приложение, в котором обсуждается безопасность исключений в стандартных классах библиотеки. Он иллюстрирует одну возможную реализацию конструктора вектора и затем просит читателя найти ошибку. Подсказка указывает на то, что это как-то связано с деструктором (возможно, с двойным освобождением?), Но я просто не вижу, что ему нужно.

Из того, что я могу сказать, распределитель мог бросить bad_alloc, завершая ctor. Аналогично, копирующий ctor на T мог бы бросить внутрь uninitialized_fill, что разрушило бы любые ранее скопированные элементы и завершило бы ctor. Если там есть ошибка, это не очевидно для меня.

Упражнение сформулировано следующим образом: «Найти ошибку в« грязной »версии конструктора вектора (Е.3.1) и написать программу, чтобы она потерпела крах. Подсказка: Сначала реализуем деструктор вектора.

Это всего лишь одно очковое упражнение, поэтому я, должно быть, упускаю что-то глупо очевидное. Я предполагаю, что это не имеет ничего общего с броском деструкторов, так как в этом случае все ставки выключены. Может быть, есть проблема с поддержанием инварианта с полями «пробел» и «последний»?

Я хотел бы услышать чьи-либо идеи.

Вот связанный код:

template<class T, class A = std::allocator<T> >
class vector {
private:
T* v;
T* space;
T* last;
A alloc;

void destroy_all();
public:
typedef size_t size_type;

explicit vector(size_type n, const T& val = T(), const A& = A());
vector(const vector& a);
vector& operator=(const vector& a);
~vector() { destroy_all(); alloc.deallocate(v, last-v); }
size_type size() const { return space-v; }
size_type capacity() const { return last-v; }
void push_back(const T&);
};

template<class T, class A>
void vector<T,A>::destroy_all() {
for(T* p = v; p != last; p++)
alloc.destroy(p);
}

template<class T, class A>
vector<T,A>::vector(size_type n, const T& val, const A& a) : alloc(a) {
v = alloc.allocate(n);
try {
std::uninitialized_fill(v, v + n, val);
space = last = v + n;
}
catch(...) {
alloc.deallocate(v, n);
throw;
}
}

3

Решение

Проблема в том, что конструктор для T может бросить, это означает, что это может бросить:

std::uninitialized_fill(v, v + n, val);

Когда это произойдет, catch(...) пункт освобождает память без должного уничтожения T объекты, которые, возможно, уже были построены.

0

Другие решения

allocator::allocate может бросить std::bad_alloc, В этом случае вы неинициализированы v и даже если вы восстановитесь из-за недостатка памяти, вы получите ошибку при уничтожении.

Еще одна проблема с std::initialized_fill. Если он выбрасывает, вы освобождаете внутреннее хранилище, не помечая указатель данных как недействительный. Деструктор снова освободит ту же память.

0

По вопросам рекламы [email protected]