Об этом полезно знать при использовании умных указателей в С++:
У shared_ptr есть конструктор, который позволяет создавать зависимости между shared_ptr'ами. (std::shared_ptr's secret constructor by Anthony Williams)
Допустим, я хочу создать указатель на объект типа Y, py, который является членом экземпляра класса X, px. Мне нужно, чтобы px не удалялся, пока я не закончу работать с py.
void bar(){
std::shared_ptr<X> px(std::make_shared<X>());
std::shared_ptr<Y> py(px,&px->y);
store_for_later(py);
} // our X object is kept alive
unique_ptr не такой уж и уникальный. (unique_ptr–How Unique is it? by Bartosz Milewski)
void f(Foo * pf) {
globalFoo = pf; // creates a global alias
}
unique_ptr<Foo> pFoo(new Foo());
f(pFoo.get()); // leaks an alias
Передача shared_ptr по значению - дорогое удовольствие. (The Real Price of Shared Pointers in C++ by Nico Josuttis)
При передаче по значению происходит его копирование и к его внутреннему счетчику прибавляется единица, это нужно сделать атомарно, что влияет на производительность. Передавайте его по ссылке, где возможно.
Зачем нужен scoped_ptr, если есть shared_ptr. (shared_ptr vs scoped_ptr)
shared_ptr "тяжелее" чем scoped_ptr, потому что он гарантирует корректную работу в многопоточных программах. Поэтому, если вы работаете с одним указателем, и вам просто нужно автоматически освободить память из-под него, лучше использовать scoped_ptr.
Чтобы корректно возвращать shared_ptr на this надо использовать enable_shared_from_this. (std::enable_shared_from_this на cppreference.com)
Пример с cppreference.com демонстрирует что случится, если вы не используете enable_shared_from_this.
#include <memory>
#include <iostream>
struct Good: std::enable_shared_from_this<Good>
{
std::shared_ptr<Good> getptr() {
return shared_from_this();
}
};
struct Bad
{
std::shared_ptr<Bad> getptr() {
return std::shared_ptr<Bad>(this);
}
~Bad() { std::cout << "Bad::~Bad() called\n"; }
};
int main()
{
// Good: the two shared_ptr's share the same object
std::shared_ptr<Good> gp1(new Good);
std::shared_ptr<Good> gp2 = gp1->getptr();
std::cout << "gp2.use_count() = " << gp2.use_count() << '\n';
// Bad, each shared_ptr thinks it's the only owner of the object
std::shared_ptr<Bad> bp1(new Bad);
std::shared_ptr<Bad> bp2 = bp1->getptr();
std::cout << "bp2.use_count() = " << bp2.use_count() << '\n';
} // UB: double-delete of Bad
Бонусная ссылка:
Smart Pointer Parameters by Herb Sutter
25 коммент.:
Если я не ошибаюсь, при копировании shared_ptr увеличение счетчика делается атомарным инкрементом (interlocked increment в VC и я не помню уже какой интринсик в gcc). Возможно на каких-то платформах это в самом деле через мьютекс сделано, но я пока таких не видел.
Vadim Panin
Если я не ошибаюсь, при копировании shared_ptr увеличение счетчика делается атомарным инкрементом
Ага, мне уже об этом писали, я поправила.
Зачем передавать const shared_ptr<T>, если можно передать T*?
Почему нельзя? Можно.
Вопрос баланса по стоимости.
Привет, Атомарные операции дороже неатомарных?
Зачем передавать const shared_ptr, если можно передать T*?
Почему бы и не T& (с проверкой на nullptr где необходимо). Избегает сырых указателей и потенциальных проблем с их сохранением или освобождением.
Для функций обработки данных, которые не влияют на владение объектами, не должно быть принципиально откуда берется объект: указатель, локальная переменная, контейнер, умный указатель, и т.п. Ссылки выглядят наиболее подходящим вариантом, за исключением, опять же, потенциальных nullptr. Передачу shared_ptr в аргументе стоит интерпретировать как явное разделение владения объектом. Аналогично для unique_ptr, его передача должна означать передачу владения.
А как на счет enable_shared_from_this<>, если Good не находится в shared_ptr<>?
Good g1;
std::shared_ptr<Good> gp2 = g1.getptr(); // всё ли тут хорошо?
2Den Raskovalov
Зачем передавать const shared_ptr<T>, если можно передать T*?
Чтобы у указателя стало на одного владельца больше.(вижу, что Alexander уже на это ответил).
Также можно передать const shared_ptr<T>&, если ты не уверен нужен тебе еще один владелец или нет, по ходу функции можно передумать и присвоить его.
2Alexander Batishchev
Привет, Атомарные операции дороже неатомарных?
Да, потому что тебе надо синхронизовать кэши ядер, нельзя менять порядок операций и пр. Джоссатис об этом подробно говорит.
2Sergey Vostrikov
А как на счет enable_shared_from_this<>, если Good не находится в shared_ptr<>?
Good g1;
std::shared_ptr<Good> gp2 = g1.getptr(); // всё ли тут хорошо?
Не хорошо. Наличие хотя бы одно shared_ptr - обязательное условие
std::enable_shared_from_this allows an object t that is currently managed by a std::shared_ptr named pt to safely generate additional std::shared_ptr instances
Алена, что вы думаете по поводу языка Rust, есть смысл его изучать? Знаний по с++ нету.
Анонимный
Алена, что вы думаете по поводу языка Rust, есть смысл его изучать? Знаний по с++ нету.
Интересный новый язык, набирает популярность. Но вакансий на сегодняшний день очень-очень мало.
Жалко в РАДИО-Т больше не приходите...
Volos_86
Жалко в РАДИО-Т больше не приходите...
Зато я недавно была в cppcast.
Алена, а вы уже не в геймдеве?
Анонимный
Алена, а вы уже не в геймдеве?
Я уже давно не в геймдеве.
Я уже давно не в геймдеве.
Простите за возможную бестактность с моей стороны, но с чем связана смена предметной области - с личными предпочтениями или с положением дел в игрострое?
AlixBZ
Простите за возможную бестактность с моей стороны, но с чем связана смена предметной области - с личными предпочтениями или с положением дел в игрострое?
Предложение работы от Майкрософта было сильно лучше всех остальных на тот момент. Причем распределенные системы не были новой для меня отраслью, я на распределенных системах специализировалась в Универе.
Эх хороший язык с++, жаль что давно забросила
> shared_ptr "тяжелее" чем scoped_ptr, потому что он гарантирует корректную работу в многопоточных программах
но есть ньюанс:
https://habrahabr.ru/post/311560/
надо батарейки к нему не забыть:
http://en.cppreference.com/w/cpp/memory/shared_ptr/atomic
./a.out
gp2.use_count() = 2
bp2.use_count() = 1
Bad::~Bad() called
Bad::~Bad() called
Не понятно почему деструктор Bad был вызван после вывода bp2.use_count() оба раза, а не один раз до и один после.
Как насчет intrusive_ptr из boost. Стратегия встроенного счетчика ссылок в текущем стандарте библиотеки не реализуется, и поэтому нужен weak_ptr поверх shared_ptr. std::make_shared конечно позволяет разместить указатели на счетчик ссылок и на объект в одном блоке памяти, и решить проблему (увеличив потребление памяти, на разного рода this). Однако пользователь API как правило понятия не имеет использовался-ли этот подход или нет (особенно если детали реализации неизвестны, есть только header файл) и вынужден использовать weak_ptr или вообще извлечь "сырой" указатель из интеллектуального. BWT, его легко извлечь из boost например вот так https://github.com/incoder1/IO/blob/master/include/config/libs/intrusive_ptr.hpp и не тащить с собой весь Boost.
Когда вернётесь в геймдев?
Когда вернётесь в геймдев?
Пока не собираюсь
Отправить комментарий