Вместо содержания
• Что?
– Писать код без new и delete в современном С++
• Как?
– Контейнеры STL
– std::make_shared, std::make_unique
– Cвои обёртки для своих умных указателей
• Почему?
– new и delete увеличивают сложность кода
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
2
Современный С++
• Современный С++ включает
– Возможности стандарта С++14
– Современные best practices
• C++14 is done!
– Рабочий черновик стандарта ISO/IEC 14882:2014(E) единогласно одобрен
– Полностью или частично поддерживается всеми мажорными компиляторами
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
3
Что и как?Общие рекомендации
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
4
Объекты в Сtypedef struct ObjectTag{// Object members
} Object;
int InitObject(Object* io_pObject);int UseObject(Object* io_pObject);int ClearObject(Object* io_pObject);
void ObjectsInC(){Object* pObject;pObject = malloc(sizeof(Object)); // Allocate memoryInitObject(pObject); // Establish invariants and acquire resourcesUseObject(pObject); // Do the workClearObject(pObject); // Release resourcesfree(pObject); // Release memory// By the way: error handling :-/
}
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
5
Объекты в С++
• Инкапсуляция:
– Конструкторы
– Деструкторы
– Методы
class Object{// Object members
public:Object(); // Establish invariants and acquire resources~Object(); // Release resourcesvoid Use(); // Do the work
};
• Обработка ошибок:
– Исключения
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
6
Объекты в С++
// 1. Naive C++ styleObject* p = new Object(); // Allocate memoryp->Use(); // Do the workdelete p; // Release memory
// 2. More secure C++98 with RAIIstd::auto_ptr<Object> ap(new Object()); // Allocate memoryap->Use(); // Do the work
// 3. More secure C++11 with RAIIstd::unique_ptr<Object> up(new Object()); // Allocate memoryup->Use(); // Do the work
// 4. New bullet-proof modern C++auto up2 = std::make_unique<Object>(); // Create unique objectup2->Use(); // Do the work
• 2 лучше, чем 1– управление ресурсами
• 3 лучше, чем 2– unique_ptr делает всё, что и
auto_ptr, только лучше
• 4 лучше, чем 3– основная тема доклада
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
7
Разделяемые объекты в С++void UseObject(Object*); // Shall retain the objectvoid AcceptObject(Object*); // Shall delete the objectvoid ShareObject(std::shared_ptr<Object>); // Both cases
// Naive C++Object* p = new Object();p->Use();UseObject(p); // Want to use object after callp->Use();AcceptObject(p); // No longer need the object
// C++98 & RAII/* No way to say this! */
// Modern C++auto sp = std::make_shared<Object>();sp->Use();ShareObject(sp); // Want to use object after callsp->Use();ShareObject(std::move(sp)); // No longer need the object
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
8
Объекты со счётчиком ссылок в С++
// Naive C++ (not used)RefCounted* p = new RefCounted();p->AddRef();p->Use();p->Release();
// C++98 & RAII RefPtr<RefCounted> rp = new RefCounted();rp->Use();
// Modern C++auto rp2 = MakeRefPtr<RefCounted>();rp2->Use();
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
9
Динамические массивы в С++
void UseArray(int*);
int n = 100;
// Naive C++int* p = new int[n];UseArray(p);delete[] p;
// C++98 & RAIIstd::vector<int> v(n);UseArray(&v[0]);
// Modern C++std::vector<int> v2(n);UseArray(v2.data());
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
10
Почему?Аргументация
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
11
Лучше == проще• Немного авторитета:
– Главный технический императив разработки ПО –управление сложностью [McConnel2004]
– Make Simple Tasks Simple! [Stroustrup2014]– ≈99.9998% разработчиков – не эксперты [Sutter2014]– Принцип KISS (Keep It Simple, Stupid)
• Что значит проще?– Простые задачи должны решаться просто– Сложные – не сложнее, чем необходимо– Сначала – простота, затем – производительность
• Как сделать проще?– Принцип DRY (Don’t Repeat Yourself)– Принцип «по умолчанию» [Sutter2014]
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
12
Принцип «по умолчанию»
• Основная мысль:
– Известная задача ⇒ известное решение
• Преимущества:
– Взаимопонимание с другими разработчиками
– Уменьшение порога входа для новичков
– Меньше думать – продуктивная лень
– Все те же, что и для правил кодирования
• “A tour of C++” [Stroustrup2013]
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
13
std::make_unique<T>() vs. std::unique_ptr(new T)
int GenerateId();
std::pair<std::unique_ptr<Object>, int> MakeObjectWithIdWrong(){return std::make_pair(std::unique_ptr<Object>(new Object()),GenerateId());
}
std::pair<std::unique_ptr<Object>, int> MakeObjectWithIdRight(){return std::make_pair(std::make_unique<Object>(), // SafeGenerateId());
}
Дублирование имени типа. DRY!
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
14
// May throw
// May leak!
std::make_shared<T>() vs. std::shared_ptr(new T)
std::shared_ptr<Object> sp(new Object());
counter
Object
sp
Два выделения памяти
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
15
std::make_shared<T>() vs. std::shared_ptr(new T)
auto sp = std::make_shared<Object>();
counter Object
Одно выделение памяти. Оптимизация We Know Where
You Live [Lavavej2012].
sp
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
16
std::make_shared и std::make_unique
• Плюсы обоих:
– Безопасность при исключениях
– Нет дублирования имени типа
• Плюсы std::make_shared:
– Одно обращение к менеджеру памяти
• Минусы есть, но несущественные:– Нет формы для указания собственного аллокатора для std::unique_ptr
(для std::shared_ptr это std::allocated_shared)
– Опасность false sharing объекта и счётчика ссылок [см. вопрос на StackOverflow]
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
17
Обобщаем
• delete по умолчанию не используется со времён RAII и стандартных контейнеров
• new по умолчанию не используется при работе со стандартной библиотекой
• Постараемся не использовать new при работе с другими библиотеками– Свои объекты со счётчиком ссылок
– Свои умные указатели
– Своё управление памятью
• Теперь и new по умолчанию не используется
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
18
Как?Некоторые подробности использования
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
19
Объекты со счётчиком ссылок
• OpenSceneGraph– Открытый 3D-движок под OpenGL на С++
– osg::Referenced – класс со счётчиком ссылок, базовый для большинства классов• ref() увеличивает счётчик
• unref() уменьшает счётчик и удаляет объект если счётчик становится равен нулю
– osg::ref_ptr<T> – умный указатель• вызывает ref() в конструкторе
• вызывает unref() в деструкторе
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
20
OSG: C++ & RAII
• Пример кода из [Wang2010, стр. 78]
• Идеально подходит для апгрейда
– Может быть выполнен автоматически
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;// Init verticesosg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;// Init normalsosg::ref_ptr<osg::Geometry> geom = new osg::Geometry;geom->setVertexArray(vertices.get());geom->setNormalArray(normals.get());// Further init geometry and use it
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
21
OSG: Modern C++
namespace osg{template<typename T, typename... Args>osg::ref_ptr<T> make_ref(Args&&... args){return new T(std::forward<Args>(args)...);
}}
auto vertices = osg::make_ref<osg::Vec3Array>(); // Init verticesauto normals = osg::make_ref<osg::Vec3Array>(); // Init normalsauto geom = osg::make_ref<osg::Geometry>();geom->setVertexArray(vertices.get());geom->setNormalArray(normals.get());// Further init geometry and use it
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
22
Своё управление памятью
• Qt
– Cross-platform application and UI framework
– QObject – класс, устанавливающий отношение родства, базовый для большинства классов
• Имеет список детей, удаляет их в своём деструкторе
• Имеет ссылку на родителя, которая может быть null
• Может менять родителя в процессе жизни
– QSharedPointer – аналог std::shared_ptr
• Предоставляет QSharedPointer<T>::create() по аналогии с std::make_shared<T>()
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
23
QObject
• Сценарии использования:
– Создание объекта в стеке
• Удаляется при выходе из блока
– Динамическое создание объекта с родителем
• Удаляется родителем
– Динамическое создание объекта без родителя
• Нужно указать родителя или удалить вручную
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
24
Qt::MakeChild
• Обёртка для безопасного сценария (см. обсуждение).
• Теперь new означает небезопасный сценарий
namespace Qt{template<class T, class... Args>T* MakeChild(Args&&... args){T* pObject = new T(std::forward<Args>(args)...);Q_ASSERT(pObject->parent() != nullptr);return pObject;
}}
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
25
Qt::MakeChild
MyWidget::MyWidget(){// Safe, created on the stackQProgressDialog progress("In progress...", "Cancel", 0, 100);
// Safe, parent is specified{// Regular, valid since C++98QPushButton* pButton = new QPushButton("Push me", this);
// Proposed, modern C++ styleauto pButton2 = Qt::MakeChild<QPushButton>("And me", this);
}
// Unsafe, parent is null, need manual deletem_pDialog = new QDialog();
}
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
26
Необходимые возможности С++14
• Возможности языка:
– Точная передача (perfect forwarding)
– Шаблоны с переменным количеством параметров (variadic templates)
• Возможности стандартной библиотеки:
– Умные указатели
– std::make_shared, std::make_unique
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
27
Можно обойтись и без них
Можно написать самому
Заключение
• Хороший код – простой код
• Принцип «по умолчанию» для упрощения
• Проблемы с delete давно известны
• Для стандартной библиотеки код без newлучше, это объявляется подходом по умолчанию – для упрощения
• new и delete теперь ассоциируются с небезопасными операциями
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
28
Спасибо за внимание!
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
29
Библиография
[McConnell2004] McConnell, Steve, and Detlef Johannis. Code complete. Vol. 2. Redmond: Microsoft press, 2004.
[Wang2010] Wang, Rui, and Xuelei Qian. OpenSceneGraph 3.0: Beginner's guide. Packt Publishing Ltd, 2010.
[Lavavej2012] Lavavej, Stephan. STL11: Magic && Secrets. Going Native, 2012.
[Stroustrup2013] Stroustrup, Bjarne. A Tour of C++. Addison-Wesley, 2013.
[Stroustrup2014] Stroustrup, Bjarne. Make Simple Tasks Simple!CppCon, 2014.
[Sutter2014] Sutter, Herb. Back to the Basics! Essentials of Modern C++ Style. CppCon, 2014.
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
30
Принцип «по умолчанию»
Задача Решение по умолчанию
Передача параметров в функцию Pass by value or const&См. [Sutter2014]
Возврат параметров из функции Return by valueСм. [Sutter2014]
Массив произвольного размера?Массив изменяющегося размера?Список произвольных объектов?
std::vector
Массив фиксированного размера std::array
Действие над каждым элементом контейнера
range-based for
Автоматическое управление динамической памятью
std::make_unique или std::make_shared
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
31
Выражение намерения
• Что сделать, а не как сделать
• Более высокоуровневый код
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
32
Память – ресурс
std∷make_unique
new/delete=
new/delete
malloc/free=
std∷fstream
fopen/fclose
Матросов Михаил, С++ без new и delete, Russian C++ User Group, Саратов, 2014
33
Top Related