set.clear () вызывает деструкторы содержащихся элементов перед удалением элемента

0

В следующем коде я бы ожидал, что утверждение пройдет, но это не так.

Это отличается от документированного поведения unique_ptr :: reset, и я нахожу это довольно неожиданным.

Я что-то делаю неправильно или это ошибка? Это проблема, потому что, если один и тот же элемент удаляется с коэффициентом усиления, деструктор вызывается дважды.

#include <set>
#include <memory>

struct F
    : std::enable_shared_from_this<F>
{
    static int destructor_count;
    static std::set<std::shared_ptr<F>> container;

    F() {}

    ~F() {
        assert(container.size() == 0);
        container.clear(); // This will delete the same pointer twice.
        destructor_count--;
    }
};

int F::destructor_count = 0;
std::set<std::shared_ptr<F>> F::container;

int main()
{
    F::container.insert(std::shared_ptr<F>(new F));
    F::container.clear();
    return 0;
}

Информация о компиляторе:

libstdС++ 6-4.6-DEV

g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  • 0
    Примечание: используйте std::make_shared .
  • 0
    @chris Крис, чтобы сэкономить на многословии?
Показать ещё 2 комментария
Теги:
g++
stl
libstdc++

3 ответа

2
Лучший ответ

Там причина unique_ptr явно документирована, чтобы вести себя таким образом: это более сильная гарантия, чем обычно для остальных стандартных библиотек. Если одно и то же правило применяется ко всем типам, то это не нужно указывать специально для unique_ptr.

Ваш код рекурсивно вызывает F::container.clear() пока выполняется другой вызов F::container.clear(). Это не гарантируется:

17.6.5.8 Reentrancy [reentrancy]
За исключением случаев, явно указанных в этом стандарте, он определяется реализацией, функции которого в стандартной библиотеке С++ могут быть рекурсивно возвращены.

Теперь, к сожалению, libstdc++ не может документировать, какие функции могут быть рекурсивно возвращены, поэтому безопаснее предполагать, что никто не может быть, поэтому ваше утверждение неверно.

1

Я не уверен, почему вы ожидаете, что это утверждение будет истинным. Вы не должны знать информацию о том, как контейнеры хранят и уничтожают память. Вы не должны делать предположений о том, что происходит во время std::set::clear(), только полагайте, что после завершения всех деструкторов будут вызваны, а std::set::size() вернет 0.

В этой старой статье д-ра Доббса STL Red-Black Trees описана структура данных поддержки позади std::set. Узлы будут удалены (и их содержимое уничтожено) до того, как все дерево будет пустым, но будьте уверены в конце std::set::clear() все деструкторы будут вызваны, а std::set::size() будет return 0.

Примечание: в других реализациях std::set могут использоваться различные структуры данных резервного копирования, красно-черные деревья - это только возможная реализация.

  • 0
    Я не вижу, как элементы хранятся влияет на вызов деструктора. Было бы просто удалить ссылки на содержащийся элемент перед их уничтожением.
  • 0
    То, что я имею в виду, состоит в том, что следует прямо сейчас удалить ссылки на узел из дерева, а затем удалить узел, а не наоборот.
Показать ещё 2 комментария
1

Деструктор F вызывается при очистке контейнера, в течение которого контейнер не пуст, поэтому утверждение не выполнено.

  • 0
    Да, но если я переберу контейнер из деструктора, возвращается указатель на частично разрушенный объект. Это отличается от поведения unique_ptr.
  • 0
    Вы говорите, если вы используете unique_ptr проход подтверждения?
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню