Две недели назад в Джэксонвилле встречался комитет стандарта ISO C++. Сегодня я хочу представить короткую сводку и написать о революционном решении, принятом на собрании в Джэксонвилле. Для получения дополнительной информации я рекомендую к прочтению статью C++ больше не будет иметь указатели. Комитет по стандартизации языка принял решение о том, что указатели будут объявлены устаревшими в C++20 и с большой долей вероятности будут удалены из C++23.
Откровенно говоря, то, что кажется революционном, — всего лишь последний шаг длинной эволюции.
Эволюция указателей в C++
Указатели существуют в C++ с самого начала. Мы получили их из C. С самого начала развития C++ всегда была тенденция сделать управление указателями более безопасным без значительных потерь.
В C++98 мы получили std::auto_ptr
для выражения исключительного владения. Но std::auto_ptr
имел большой изъян. Когда вы копирует std::auto_ptr
, владение ресурсом передавалось копии. Копирование выглядело как перемещение. Изображение ниже показывает неприятное поведение std::auto_ptr
.
Это было очень плохо, приводило к множеству серьёзных багов. Поэтому мы получили std::unique_ptr
в C++11, и объявили std::auto_ptr
устаревшим в C++11, и окончательно удалили из C++17. Дополнительно мы получили std::shared_ptr
и std::weak_ptr
в C++11 для управления владением. Вы не можете копировать, но можете перемещать std::unique_ptr
, и если копируете или присваиваете std::shared_ptr
, счётчик ссылающихся указателей увеличивается. Посмотрите сюда:
Начиная с C++11 C++ имеет многопоточную библиотеку. Это делает управление std::shared_ptr
достаточно сложным, потому что std::shared_ptr
по определению разделяемое, но не потоко-безопасное. Только контрольная часть со счётчиками является потоко-безопасной, но не доступ к адресу контролируемого ресурса. Это значит, что изменение счётчика — атомарная операция, но вы не имеете гарантии, что ресурс будет удалён ровно один раз. По этой причине мы получаем в C++20 атомарные умные указатели: std::atomic_shared_ptr
и std::atmic_weak_ptr
. Про детали предложений комитета стандартизации читайте здесь: Атомарные умные указатели.
Теперь переходим к более интересным частям будущих стандартов C++20 и C++23. Указатели будет объявлены устаревшими в C++20 и удалены из C++23. Скажем три слова: Нет Новому New (NNN).
std::unique_ptr спасёт нас
Но подождите, как же догма C++: Не платить за то, что вам не нужно. Как мы сможем программировать без указателей? Просто используйте std::unique_ptr
. Из своего дизайна std::unique_ptr
такой же быстрый и экономный, как и обычный указатель, и имеет явное преимущество — автоматическое управление ресурсом.
Ниже простой тест производительности.
// all.cpp
#include <chrono>
#include <iostream>
static const long long numInt= 100000000;
int main(){
auto start = std::chrono::system_clock::now();
for ( long long i=0 ; i < numInt; ++i){
int* tmp(new int(i));
delete tmp;
// std::shared_ptr<int> tmp(new int(i));
// std::shared_ptr<int> tmp(std::make_shared<int>(i));
// std::unique_ptr<int> tmp(new int(i));
// std::unique_ptr<int> tmp(std::make_unique<int>(i));
}
std::chrono::duration<double> dur= std::chrono::system_clock::now() - start;
std::cout << "time native: " << dur.count() << " seconds" << std::endl;
}
Эта программа выделяет и освобождает память для 100 миллионов int
. Я использую указатели, std::shared_ptr
и std::unique_ptr
в двух вариациях. Я компилирую программу с и без максимальной оптимизации в Linux и в Windows. Получаются такие числа:
Две вариации std::unique_ptr
на Linux и Windows показывают такую же производительность, как обычные указатели. За деталями этого теста обратитесь к моей прошлой статье: Потребление памяти и производительность умных указателей.
Семантика владения
Честно говоря, мы используем указатели и, в частности, обычные указатели очень часто. Вопрос, должны ли вы использовать указатель, сводится к следующему: Кто владелец? К счастью, с помощью кода мы можем чётко выразить это.
- Локальные объекты. Рантайм C++ как владелец автоматически управляет жизнью таких ресурсов. То же самое относится к глобальным объектам или членам класса. Справочники сводят это к области видимости.
- Ссылки: я не владелец. Я только обеспечиваю, что ресурс не может быть пустым.
- Обычные указатели: я не владелец. Я только ссылаюсь на ресурс, если он есть. Я не должен удалять ресурс.
- std::unique_ptr: я исключительный владелец ресурса. Я могу явно освободить мой ресурс.
- std::shared_ptr: я разделяю ресурс с другими
std::shared_ptr
. Я могу явно удалить мой разделяемый ресурс, если он больше никому не нужен. - std::weak_ptr: я не владелец ресурса, но я могу временно разделять ресурс при вызове моего метода
std::weak_ptr::lock
.
Нам нужно будет изменить только одну из шести практик использования указателей и мы рады следующему шагу в развитии C++.
Автор: sergio_nsk