LINUX.ORG.RU

Корректная очистка контейнеров в c++11

 


0

1

Всем привет. Хочу организовать вектор из пар обектов типов A и B и корректно его очищать. Верной ли будет конструкция:

{
...
	list<unique_ptr<pair<unique_ptr<A>, unique_ptr<B>>>> pairsList {
		(new pair(new A(), new B())),
		...
	};
...
}
с учётом, что на выходе из блока всё освободится корректно? Если нет - как сделать элегантно без ручного итерирования?

★★★★★

Ответ на: комментарий от UVV

Пар будет десяток-другой. Код будет работать для UT, поэтому на производительность тут можно болт положить по идее, да и пары в таком ключе объявлять удобнее.

jcd ★★★★★
() автор топика
Ответ на: комментарий от asaw

Не догоняю, предложение состоит в:

	pair<unique_ptr<A>, unique_ptr<B>> newPair1(new A(), new B());
	list<unique_ptr<pair<unique_ptr<A>, unique_ptr<B>>>> pairsList {
		newPair1,
		...
	};
?

jcd ★★★★★
() автор топика
Ответ на: комментарий от asaw

Полагаю, в initializer_list просто так pair<unique_ptr<A>,unique_ptr<B>> не засунешь. Поскольку компилятор тут будет пытаться использовать конструкторы копирования pair(const pair&), а его нет, т.к. нет конструктора копирования для unique_ptr.

eao197 ★★★★★
()
Ответ на: комментарий от asaw

GCC 5.2.0 ругается при попытке инициализации list<pair<unique_ptr<A>,unique_ptr<B>>> через initializer_list. Если же заполнять список через emplace_back, то все нормально.

Другое дело, что ТС, вероятно, вообще не нужны unique_ptr и можно обойтись pair<A,B> или tuple<A,B>.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Да, век живи - век учись. Интересно, почему в реализации конструктора с initializer list используется push_back(), а не emplace_back()?

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 1)
Ответ на: комментарий от asaw

Интересно, почему в реализации initializer list используется push_back(), а не emplace_back()?

Я не настолько копенгаген в деталях реализации работы с initializer list в компиляторе и деталях реализации конструкторов std::list, чтобы ответить на этот вопрос :(

eao197 ★★★★★
()
Ответ на: комментарий от lovesan

Да, всё догнал. Поправил на

list<shared_ptr<pair<A, B>>> testPairsList;
testPairsList.emplace_back(new A(), new B());

Но вопрос остаётся в силе: как удалить при этом все объекты пар, хранящиеся в shared_ptr-ах в листе?

jcd ★★★★★
() автор топика
Ответ на: комментарий от jcd

Зачем вам вообще создавать A и B в динамической памяти через new?

PS. lovesan не слушайте, он в C++ разбирается как свинья в апельсинах.

eao197 ★★★★★
()
Ответ на: комментарий от jcd

Могу ошибаться, но ИМХО там new вообще не нужно.

А если так? (не проверял)

list<pair<A, B>> testPairsList;
testPairsList.emplace_back(std::make_pair(A(),B());

Kroz ★★★★★
()
Ответ на: комментарий от lovesan

shared_ptr не нужно пихать куда не попадя.

UVV ★★★★★
()
Ответ на: комментарий от jcd

Давайте поступим проще.

Объекты A и B вам может потребоваться создавать динамически в нескольких случаях:

  • эти объекты слишком большие (мегабайты, десятки мегабайт) и их копирование слишком накладно;
  • эти объекты не допускают копирования в принципе (например, хранят в себе хендлы внешних ресурсов, как std::ifstream);
  • на самом деле A и B — это базовые классы/интерфейсы, а создаются наследники этих классов (через фабрики, к примеру).

Если имеет место один из данных случаев, то в контейнере вам нужно хранить A и B через какой-то умный указатель. В C++11 таких два:

  • unique_ptr, который означает эксклюзивное владение объектом. Объект будет разрушен как только будет разрушен unique_ptr. Это очень эффективный умный указатель, но он не может использоваться, если вам нужно иметь живые ссылки на A и B в разных местах программы, а не только в самом list-е;
  • shared_ptr, который означает совместное владение объектом. Он не так эффективен как unique_ptr (есть атомарные инкременты/декременты), но зато можно иметь ссылки на A и B в разных местах;

Получается, что если вам нужны именно динамически созданные объекты A и B, и время их жизни определяется временем жизни list-а, в который они помещены, то достаточно иметь list<pair<unique_ptr<A>,unique_ptr<B>>>.

Если нужны динамически созданные объекты A и B, но время их жизни может превышать время жизни list-а, в который они помещены, то нужен list<pair<shared_ptr<A>,shared_ptr<B>>>.

Если же вам вообще не нужно создавать A и B динамически (а во многих случаях как раз не нужно), то достаточно иметь list<pair<A,B>>. При этом память для хранения A и B будет создаваться и освобождаться самим list-ом автоматически, без вашего участия. И вам вообще не нужно думать о том, где A и B размещены.

Вот как-то так, если совсем схематически, хотя могут быть некоторые нюансы.

eao197 ★★★★★
()
Ответ на: комментарий от jcd

Зачем это очищать? Оно само очистится при выходе из области видимости. Причем, во всех перечисленных случаях.

asaw ★★★★★
()
Ответ на: комментарий от asaw

Каким образом? Деструктор листа вызывает delete для всех входящих в него элементов? Это фича С++11, или что?

jcd ★★★★★
() автор топика
Ответ на: комментарий от eao197

Объекты A и B вам может потребоваться создавать динамически в нескольких случаях:

Смарт поинтеры я хотел использовать исключительно чтобы каким-нибудь образом не очищать список руками, иначе динамическая память мне здесь разумеется не нужна.

jcd ★★★★★
() автор топика
Ответ на: комментарий от jcd

Вам не нужно очищать содержимое списка руками. Список сам все это делает в нужных местах (деструктор, операции erase, операции копирования и т.д.).

eao197 ★★★★★
()
Ответ на: комментарий от jcd

Каким образом? Деструктор листа вызывает delete для всех входящих в него элементов?

delete он не вызовет, но поскольку элементы живут в листе, то для каждого будет вызван деструктор при разрушении листа. На то он и контейнер. Если элемент автоматический указатель, то он вызовет delete (или что ты ему укажешь - хоть лямбду).

Это фича С++11, или что?

Нет, это фича C++ с самого начала.

asaw ★★★★★
()
list<pair<A,B>> pairs {
    { x, y },
    ...
};

тред не читал

anonymous
()

(new pair(new A(), new B()))

Это эталон отстрела ног из учебника. Порядок вызова new не гарантирован, exception может сработать во втором из них - теряя ссылку на первый навсегда.

vertexua ★★★★★
()

new A()

Можно просто new A. Но нужно просто A().

anonymous
()
Ответ на: комментарий от asaw

Нет, это фича C++ с самого начала.

Где про это можно почитать подробнее? Сколько пользуюсь, а объекты в коллекциях до сих пор не держал.

jcd ★★★★★
() автор топика

Ты делаешь какую то хрень

list<pair<A, B>> pairsList {
    make_pair(A(), B(),
    //...
}
Почему не так?

CatsCantFly
()

И вообще: И эти люди говорят потом что плюсы неудобные и кривые, когда просто использовали их через одно место

CatsCantFly
()
Ответ на: комментарий от CatsCantFly

И вообще

Походу, эти люди просто плюсы никогда не изучали толком.

eao197 ★★★★★
()
Ответ на: комментарий от jcd

Ну там как-то кратко, наверное, потому, что это очевидно, но и там вот есть:

A class with the main purpose of holding objects is commonly called a container.

Like all standard-library containers, v ector is a container of elements of some type T; that is, a v ector<T>. Just about any type qualifies as an element type: built-in numeric types (such as char, int, and doub le), user-defined types (such as str ing, Entr y, list<int>, and Matr ix<double,2>) and pointers (such as const char∗, Shape∗, and doub le∗). When you insert a new element, its value is copied into the container. For example, when you put an integer with the value 7 into a container, the resulting element really has the value 7. The element is not a reference or a pointer to some object containing 7. This makes for nice compact containers with fast access. For people who care about memory sizes and run-time performance this is critical.

asaw ★★★★★
()
Ответ на: комментарий от CatsCantFly

Ну да, если пытаться использовать плюсы как жабку и сишарп - они жутко неудобные.

anonymous
()

Можно и еще короче:

list<pair<A, B>> pairsList{
    { A(), B() },
    //...
};

CatsCantFly
()
Ответ на: комментарий от SystemD-hater

Для наглядности. Так и знал, что придет какой-нить «умник» и на это покажет. Хотел посмотреть кто это будет.

asaw ★★★★★
()
Ответ на: комментарий от SystemD-hater

Сверхманёвры детектед.

Вот представь себе специально посмотрел сигнатуру std::make_pair в C++11 и написал.

asaw ★★★★★
()

Но вопрос остаётся в силе: как удалить при этом все объекты пар, хранящиеся в shared_ptr-ах в листе? как сделать элегантно без ручного итерирования?

Пул используй с placement new и не насилуй моск «корректными очистками» и прочим преодолением собственноручно созданных трудностей.

slackwarrior ★★★★★
()

Хочу организовать вектор

list<unique_ptr

вектор на основе list? ты на верном пути :)))

slackwarrior ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.