Мне любопытно, поскольку я начинаю принимать больше идиом повышения и того, что кажется передовым опытом. Интересно, в какой момент мой С ++ хоть отдаленно похож на С ++ прошлых лет, часто встречающийся в типичных примерах и в умах тех, кто Еще не знакомы с "Современным C++"?





В наши дни я практически отказался от использования необработанных указателей. Я даже начал просматривать нашу базу кода в поисках мест, где использовались необработанные указатели, и переключил их на вариант с интеллектуальным указателем. Удивительно, сколько кода мне удалось удалить, выполнив это простое действие. На управление временем жизни необработанных указателей C++ тратится так много кода.
Единственные места, где я не использую указатели, - это пара сценариев взаимодействия с другими базами кода, которые я не контролирую.
Я по-прежнему использую обычные указатели в ресурсо-зависимом коде или другом коде, который требует крошечного следа, например, в некоторых исключениях, где я не могу предположить, что какие-либо данные действительны, и должен также предполагать, что у меня тоже заканчивается память.
В противном случае управляемая память почти всегда превосходит необработанную, потому что это означает, что вам не нужно иметь дело с ее удалением в нужном месте, но вы по-прежнему имеете большой контроль над точками построения и разрушения ваших указателей.
О, и есть еще одно место, где можно использовать необработанные указатели:
boost::shared_ptr<int> ptr(new int);
Нет огромных накладных расходов на классы указателей с ограниченной областью видимости, но они должны существовать, а общие указатели нуждаются в подсчете ссылок и связанных с ними механизмах. Однако я всегда следую правилу 1; сначала профиль, а затем оптимизируйте.
Не могу вспомнить ни единого случая за 15 лет программирования на C++, который мне когда-либо требовался для явного выделения одного int в куче. Другое дело - массив int: s, но тогда всегда есть std :: vector <int>.
Если у вас есть круговые структуры данных, например, A указывает на B, а B указывает обратно на A, вы не можете наивно использовать интеллектуальные указатели как для A, так и для B, поскольку тогда объекты будут освобождены только от дополнительной работы. Чтобы освободить память, вам нужно вручную очистить интеллектуальные указатели, что примерно так же плохо, как удаление, от которого избавляются интеллектуальные указатели.
Вы можете подумать, что это происходит не очень часто, но предположим, что у вас есть родительский объект, который имеет интеллектуальные указатели на группу дочерних объектов. Где-то по пути кому-то нужно найти Родителя для Дочернего, поэтому они добавляют элемент интеллектуального указателя в Дочерний, который указывает обратно на родителя. Молча память больше не освобождается.
Требуется некоторая осторожность. Умные указатели не эквивалентны сборке мусора.
Для этого нужен boost :: weak_ptr, если он у вас есть. Таким образом, вы все равно можете использовать интеллектуальные указатели, а не необработанные, просто не все интеллектуальные указатели будут shared_ptr.
Абсолютно boost :: weak_ptr - это еще один способ решить проблемы с круговой ссылкой, но я думаю, что моя точка зрения все еще остается в силе: вы должны обращать внимание, а не просто слепо использовать любые умные указатели, которые у вас есть.
Полагаю, я предполагаю, что когда спрашивающий говорит «принятые интеллектуальные указатели boost», он имеет в виду их все, используемые надлежащим образом, а не только shared_ptr, используемый для всего. Вы правы, что не все читатели это оценят :-)
Я почти не использую shared_ptr, потому что вообще избегаю совместного владения. Поэтому я использую что-то вроде boost::scoped_ptr, чтобы «владеть» объектом, но все другие ссылки на него будут необработанными указателями. Пример:
boost::scoped_ptr<SomeType> my_object(new SomeType);
some_function(my_object.get());
Но some_function будет иметь дело с необработанным указателем:
void some_function(SomeType* some_obj)
{
assert (some_obj);
some_obj->whatever();
}
Обидите меня - я собирался сказать, что передача по необработанному указателю может использоваться по соглашению, чтобы подразумевать, что вызываемый объект не будет сохранять ссылку на объект (или, по крайней мере, не таким образом, который влияет на управление ресурсами).
Как сохранить ссылку на объект таким образом, чтобы это не повлияло на управление ресурсами? Как только ресурс уничтожен, хранитель этой ссылки должен теперь знать об этом факте, верно?
Все дело в объемах. Поместите владельца в крайнюю область видимости, в которой будет использоваться объект, а затем просто передайте ему указатели в этой области. Никакая ссылка не может существовать в более широкой области, чем владелец, и, таким образом, она гарантированно указывает на действительный объект.
Почему бы вам не передать scoped_ptr по ссылке?
Потому что, если some_function () принимает необработанный указатель, я также могу создать my_object в стеке и просто передать его адрес в some_function (). фактически, я бы так и поступил в большинстве случаев.
@Patrick: я думаю, что передача scoped_ptr может быть немного неудобной по сравнению с необработанным указателем. Если позже вы захотите провести рефакторинг, чтобы переданная вещь была const, тогда изменение будет с scoped_ptr <T> на scoped_ptr <const T>, а не с T * на const T *. Это не одна из сильных сторон шаблонов C++.
Ссылайтесь на людей! Если вы не хотите явно разрешить вызов функции с nullptr, я бы сказал, что ссылки - это путь. Зачем обнаруживать ошибку во время выполнения, если компилятор с радостью сделает это за вас?
Я по-прежнему использую необработанные указатели на устройствах с отображением ввода-вывода с отображением памяти, таких как встроенные системы, где наличие интеллектуального указателя на самом деле не имеет смысла, потому что вам никогда не понадобится и не будет возможности delete.
Я считаю, что основное различие между «современным» C++ и старым * состоит в осторожном использовании инвариантов классов и инкапсуляции. Хорошо организованный код, естественно, имеет меньше летающих указателей. Я почти так же нервничаю плавать в shared_ptrs, как если бы я был в новостях и удалял.
Я с нетерпением жду unique_ptr на C++ 0x. Я думаю, что это уберет несколько (умных) указателей, которые все еще бродят по дикой природе.
* все еще, к сожалению, очень часто
Конечно, всякий раз, когда вы имеете дело с устаревшей библиотекой или API, вам нужно будет передать необработанный указатель, хотя вы, вероятно, просто временно извлечете его из своего интеллектуального указателя.
Фактически, всегда безопасно передавать необработанный указатель на функцию, если функция не пытается сохранить копию указателя в глобальной переменной или переменной-члене или попытаться удалить ее. При наличии этих ограничений функция не может влиять на время жизни объекта, и единственная причина использования интеллектуального указателя - управлять временем жизни объекта.
Я пишу C++, который должен сосуществовать с Objective C (используя Objective C++ для моста). Поскольку объекты C++, объявленные как часть классов Objective C++, не имеют вызываемых конструкторов или деструкторов, вы не можете удерживать их там в интеллектуальных указателях.
Поэтому я предпочитаю использовать необработанные указатели, хотя часто с boost :: intrustive_ptr и внутренним счетчиком ссылок.
Это правильно? В моем чтении документа говорится, что вызывается конструктор без аргументов, а деструктор вызывается при уничтожении объекта Objective C. Возможно, что-то изменилось / улучшилось с тех пор, как вы опубликовали.
Вещи изменились / улучшились с тех пор, как я опубликовал :-) На самом деле, с тех пор, как я опубликовал - я уже немного устарел.
Не то чтобы я бы это сделал, но вам нужны необработанные указатели, чтобы реализовать, скажем, связанный список или график. Но гораздо разумнее было бы использовать std::list<> или boost::graph<>.
Всего несколько штук в голове:
Общей чертой здесь является «любая ситуация, в которой вам нужно рассматривать часть памяти как нечто иное, чем ресурс, выделение которого у вас есть».
Всякий раз, когда мне нужно выделить память (скажем, чтобы сохранить BITMAPINFOHEADER с дополнительными байтами в конце), я бы использовал std :: vector, если это возможно, тогда не о чем беспокоиться, и если Windows API сообщит мне мою выделенную память недостаточно, std :: vector :: resize () сладко.
Конечно. Вы по-прежнему можете использовать стандартные контейнеры или интеллектуальные указатели для управления базовой памятью, когда это возможно. Дело в том, что когда вы собираетесь поговорить с Windows API, вы собираетесь привести его к необработанному указателю, о чем и был задан исходный вопрос.
Также для взаимодействия с «внешним миром». Если ваш пользователь запрашивает буфер, он получает буфер, а не умный указатель.
Я думал, что у классов boost: pointer нет накладных расходов? Неужели компилятор все не оптимизирует?