LINUX.ORG.RU

Умные указатели Стоит ли их использовать?

 


0

3

Всем привет, начал переходить на новый стандарт C++11 и сейчас думаю, стоит ли переходить на умные указатели? Запись получается какая-то громоздкая, это мне не нравится, ну и еще боюсь напортачить с ними, а с классическими указателями как-то работается спокойно и комфортно, вот и не знаю стоит ли вообще пытаться.

★★★

тебе стоит не использовать C++ вообще

anonymous
()

Изучи доступные типы умных указателей и применяй там, где они подходят. Вообще, генеральная линия партии^Wразвития стандарта в том, чтобы в С++ можно было все сделать без применения простых указателей.

Запись получается какая-то громоздкая

Используй auto

боюсь напортачить

Не будешь пользоваться - так и будешь бояться.

с классическими указателями как-то работается спокойно и комфортно

Пока не начинаются крэши и утечки :)

annulen ★★★★★
()

Это нужно было сделать ещё 15 лет назад. Начиная со своих, потом бустовских, и заканчивая стдешными из нового стандарта. :)

BRE ★★
()

Всем привет, начал переходить на новый стандарт C++11 и сейчас думаю, стоит ли переходить на умные указатели?

да. С ними ошибок меньше. Я уже давно перешёл, только я раньше свои велосипеды делал, а сейчас нужда в велосипедах отпала.

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

Я 15 лет назад еще не прогал :) Да и на С++ перешел относительно недавно, 3 года назад, до этого писал на дельфи/паскаль 7 лет.

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

Ясно, буду пробовать переходить на них тогда, а если внутри класса у меня есть умные указатели, и класс сам умный указатель: shared_ptr то при удалении класса у меня и умные указатели удаляться?

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

Разумеется (если, конечно, других копий shared_ptr не осталось).

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

Мне лень, а тут по быстрому спросить можно ))

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

Да, всегда (ну почти).

mix_mix ★★★★★
()

Всем привет, начал переходить на новый стандарт C++11 и сейчас думаю, стоит ли переходить на умные указатели?

Умные указатели и без c++11 были не лишними, а полезными.

andreyu ★★★★★
()

Почитай последнюю книженцию маерса, есть сам знаешь на каком файлообменнике например(в оч хорошем качестве).

Только главу про pimpl не читай, по моему там всё мимо :)

pon4ik ★★★★★
()

стоит ли переходить на умные указатели?

LOL.

Запись получается какая-то громоздкая

Да уж поменьше, чем вручную писать (учитывая delete).

DarkEld3r ★★★★★
()

стоит ли переходить на умные указатели

Разумеется. Умные указатели решают как минимум три проблемы по сравнению с сырыми:

  • Автоматическое детерминированное удаление
  • Явная семантика владения
  • Exception safety

боюсь напортачить с ними

Если научишься их правильно использовать, компилятор не даст тебе напортачить.

Во-первых в 90% случаев тебе будут нужны std::unique_ptr в сочетании с невладеющими сырыми указателями. Здесь нужно почитать про семантику переноса и std::move. Обильное использование std::shared_ptr в коде - обычно признак непонимания умных указателей.

Во-вторых, забудь про new/delete (кроме низкоуровневого кода и различных оберток). Для создания объектов используй std::make_unique и std::make_shared.

Если будешь следовать этим простым правилам, большинство потенциальных проблем отловит компилятор и настучит тебе по рукам.

archie
()

если они умные, то они сами тебя используют

buratino ★★★★★
()

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

Запусти свой код под valgrind или address sanitizer, узнаешь.

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

Да я сразу узнал, и вообще оффтоп.

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

Я уже прочитал про unique_ptr и shared_ptr. Но я не понял зачем unique_ptr, он не подсчитывает ссылки а просто от одной переменной ходит к другой, где их использовать пока я не понял, а вот shared_ptr уже использую, у меня в 2х стеках находятся объекты и во втором объекты ссылаются на первый стек, при очистке второго стека, сами объекты не должны удаляться. Тут наверное не применишь unique_ptr.

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

Ну а ты сам читал?

Меййерс норм мужык, но лично я не осилил в чём профит pimpl с uniq_ptr, ибо буков получается больше, а с точки зрения безопасности всё остаёться на одном уровне. Единственный кейс который мне видиться, это когда у тебя овердофига (>10) конструкторов...

pon4ik ★★★★★
()

Запись получается какая-то громоздкая, это мне не нравится

Если это единственное, что тебе не нравится, сделай короткие элиасы и используй их. В рабочих проектах конечно это не везде подойдет, а в личных я например так и делаю:

template<typename T>
using Box = std::unique_ptr<T>;

template<typename T>
using Rc = std::shared_ptr<T>;

template<typename T>
using Ref = std::weak_ptr<T>;

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

Но я не понял зачем unique_ptr, он не подсчитывает ссылки а просто от одной переменной ходит к другой, где их использовать пока я не понял

Использовать надо там, где подсчет ссылок не нужен, т.е. в большинстве случаев. Например есть родительское окно, содержащее массив умных указателей на дочерние виджеты. У каждого виджета есть ровно один владелец - окно. При уничтожении окна должны автоматически уничтожиться все дочерние виджеты. Значит здесь нужен unique_ptr.

Пример на shared_ptr - это несколько 3д моделей, ссылающихся на одну и ту же текстуру. У текстуры одновременно есть несколько владельцев. Когда умрет последняя модель, только тогда уничтожится текстура. Вот это типичный пример на shared_ptr.

Главное - не пихать shared_ptr туда, где они по смыслу не нужны. Это только сбивает с толку читающего. Ну и shared_ptr вносит оверхед и лишние аллокации, а unique_ptr полностью бесплатны.

Вечером могу привести пример на идиоматичное использование unique_ptr, если нужно.

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

unique_ptr

Автоматические уничтожение при выходе из зоны видимости или в деструкторе. unique_ptr более легкий, чем shared_ptr.

И в некоторых случаях некоторые объекты нельзя копировать (не технически, а логически), иначе будет нарушение логики работы программы.

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

Понятно, спасибо за подробное расписание!

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

А можно же комбинировать получается, допустим у меня все элементы gui ссылаются на uiManager, можно тогда сам uiManager сделать как std::uniquie_ptr а у элементов гуя сделать параметр допустим : UIManager *manager; и они просто ссылаются на него. А сам uiManager удаляется когда удаляется класс Core. Сами элементы никак не изменяют параметры uiManager, только получают от него параметры.

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

Блин что-то я опять запутался :D В твоем примере родительское окно содержит виджеты, получается виджеты - это unique_ptr а окно shared_ptr потому-что много виджетов ссылаются на это окно, я правильно понял? Тогда в моем примере лучше uiManager сделать shared_ptr а внутри элементы uniquie_ptr. Шейдеры для отрисовки допустим гуя тоже делать shared_ptr как и текстуру skin, потому-что абсолютно все элементы гуя ссылаются на эту текстуру как и на шейдеры colorShader и atlasShader.

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

В твоем примере родительское окно содержит виджеты, получается виджеты - это unique_ptr а окно shared_ptr потому-что много виджетов ссылаются на это окно, я правильно понял?

Неа, не совсем. Окно содержит массив unique_ptr на дочерние виджеты, а вот дочерние виджеты ссылаются на окно через обычный указатель. Обычные указатели никто не отменял, они все равно нужны, просто они невладеющие.

Еще раз. Есть владеющие ссылки. Это когда один объект является хозяином другого и удаляет его в конце своей жизни. И есть невладеющие ссылки. Это когда один объект просто юзает другой, но не является его хозяином и не должен его удалять.

Если объект единолично владеет другим объектом, то он ссылается на него через unique_ptr. Если несколько объектов совместно владеют другим объектом, то они ссылаются на него через shared_ptr. Во всех остальных случаях - либо обычные указатели, либо weak_ptr.

Тогда в моем примере лучше uiManager сделать shared_ptr а внутри элементы uniquie_ptr.

Опять же, элементы гуя не владеют uiManager, поэтому они должны ссылаться на него обычными указателями. А вот Core владеет uiManager и должен ссылаться на него через unique_ptr.

Шейдеры для отрисовки допустим гуя тоже делать shared_ptr как и текстуру skin

А вот это правильно. Элементы гуя совместно владеют шейдерами/текстурами и ссылаются на них через shared_ptr. Когда последний элемент гуя помрет и счетчик ссылок дойдет до нуля, тогда удалятся связанные с ним шейдеры/текстуры.

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

Держи еще пример кода с unique_ptr.

class Widget
{
public:

	void setParent(Widget *parent)
	{
		m_parent = parent;
	}

	void addWidget(std::unique_ptr<Widget> widget)
	{
		widget->setParent(this);
		m_widgets.push_back(std::move(widget));
	}

	Widget *getWidget(int index) const
	{
		return m_widgets[index].get();
	}

	std::unique_ptr<Widget> takeWidget(int index)
	{
		auto widget = std::move(m_widgets[index]);
		m_widgets.erase(m_widgets.begin() + index);
		widget->setParent(nullptr);
		return widget;
	}

private:

	Widget *m_parent = nullptr;
	std::vector<std::unique_ptr<Widget>> m_widgets;
};

Юзать вот так. Обрати внимание, как child передается туда-сюда между объектами через std::move.

int main()
{
	Widget window;

	// создаем дочерний виджет, текущий владелец - функция main()
	auto child = std::make_unique<Widget>();

	// добавляем виджет в окно, теперь оно его новый владелец, child становится nullptr
	window.addWidget(std::move(child));

	// получаем обычную ссылку для пользования
	Widget *childRef = window.getWidget(0);

	// отбираем владение виджетом у окна, теперь его владелец снова функция main()
	child = window.takeWidget(0);

	// добавляем виджет в другое окно
	Widget window2;
	window2.addWidget(std::move(child));

	return 0;
}
archie
()

Не стоит Си++ использовать в наше время ВООБЩЕ!111

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

Размер на стеке будет 2*sizeof(void*), вместо sizeof(void*) у unique_ptr/raw pointer, плюс таблица с reference counters выделяется в куче, а значит еще одна аллокация, фрагментация кучи и занятая память. make_shared позволяет избежать второй аллокации, что чуть лучше.

Разыменовывание shared_ptr бесплатное, cами счетчики изменяются только в конструкторе копирования и деструкторе, так что по возможности лучше передавать по ссылке или двигать.

В общем ненужного оверхэда хватает, по возможности нужно всегда использовать бесплатный unique_ptr.

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

Я почему уточнил что в многопоточном, ведь shared_ptr не знает что он работает в однопоточном приложении и будет тратить время на некие синхронизации, так как он thread safe. Или он как-то удешевляет свою работу если нету contention?

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

shared_ptr использует атомики для подсчета ссылок, так что оверхэд при отсутствии конфликтов относительно небольшой, это не мьютекс. Сам подсчет ссылок идет только в конструкторе копирования и деструкторе, так что если не передавать shared_ptr по значению и не создавать их на каждый чих, оверхэд от потокобезопасности должен быть минимальным.

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

Не моглы бы вы прояснить по поводу std::move. Чет я тоже запутался. К примеру выполнили мы:

auto child = std::make_unique<Widget>();

как я понимаю мы создали объек тыпа unique_ptr. Класс этого объекта не содержит конструктора копий так как логично что этот объект не должен быть копируемым (на то он и uniqe), но нам нужно передать его в качестве аргумента. Так как класс unique_ptr содержит move constructor то мы его и используем.

В констекте языка с++ объект child является lvalue? Тоесть объекты класса unique_ptr являются lvalue? Функторы к примеру являются lvalue? Как я понимал, если мы имеем выражение которое имеет адресс по которому мы можем получить результат, значит это lvalue.

Тогда получается что std::move используется для того что бы превратить lvalue в rvalue чтобы был вызван move конструктор? И гарантировать что child после этого будет null_ptr ? Или чтото другое. Спасибо. Для меня это тоже очень туманная тема.

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

Но стоит признать что атомики сбрасывают кеши процессора, ведь правильно? Тоесть в любом случае оверхед не нулевой.

С передачей по ссылке все понятно

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

нам нужно передать его в качестве аргумента. Так как класс unique_ptr содержит move constructor то мы его и используем

Это если тебя функция-«sink», которая забирает владение указателем, и он после вызова становится недействительным. Если надо передать указатель в фунцию, но сохранить владение над ним поле вызова, всегда можно передавать по ссылке.

В констекте языка с++ объект child является lvalue?

Rule of thumb - если можно взять адрес, то это lvalue. От child можно взять адрес, это lvalue.

Тоесть объекты класса unique_ptr являются lvalue?

Тип объекта не связан с lvalue/rvalue. Любой именованный объект является lvalue, любой временный - rvalue. В контринтуитивном случае функции с сигнатурой «void foo(Bar&& bar)» bar - это lvalue.

Тогда получается что std::move используется для того что бы превратить lvalue в rvalue чтобы был вызван move конструктор? И гарантировать что child после этого будет null_ptr ?

Да, только «превратить» может подразумевать какое-то действие, лучше «интерпретировать». Вообще std::move это обычный статик каст:

template<typename _Tp>
    constexpr typename std::remove_reference<_Tp>::type&&
    move(_Tp&& __t) noexcept
    { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
anonymous
()
Ответ на: комментарий от archie

Очень интересно, вроде теперь понял как правильно работать, спасибо огромное, что на пальцах все рассказали )

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

если использовать qt, то умные указатели особо и не нужны ибо есть qobject

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

Ну не то что бы прям гарантирует, но способствует:)

Я не против pimpl, я даже не против метода который декларирует Мейерс, просто я против рекомендации всегда так делать, ибо в как раз в 90% случаев в его подходе получается больше буков, без видимого профита.

Я почти уверен что pimpl, он выбрал дабы показать ещё неупомянутые фичи нового стандарта, прост пример не особо удачный.

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