[Перевод] Без new: Указатели будут удалены из C++

Две недели назад в Джэксонвилле встречался комитет стандарта ISO C++. Сегодня я хочу представить короткую сводку и написать о революционном решении, принятом на собрании в Джэксонвилле. Для получения дополнительной информации я рекомендую к прочтению статью C++ больше не будет иметь указатели. Комитет по стандартизации языка принял решение о том, что указатели будут объявлены устаревшими в C++20 и с большой долей вероятности будут удалены из C++23.


Откровенно говоря, то, что кажется революционном, — всего лишь последний шаг длинной эволюции.


image
Эволюция указателей в C++

Указатели существуют в C++ с самого начала. Мы получили их из C. С самого начала развития C++ всегда была тенденция сделать управление указателями более безопасным без значительных потерь.


В C++98 мы получили std::auto_ptr для выражения исключительного владения. Но std::auto_ptr имел большой изъян. Когда вы копирует std::auto_ptr, владение ресурсом передавалось копии. Копирование выглядело как перемещение. Изображение ниже показывает неприятное поведение std::auto_ptr.
image


Это было очень плохо, приводило к множеству серьёзных багов. Поэтому мы получили 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, счётчик ссылающихся указателей увеличивается. Посмотрите сюда:
image


Начиная с 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 
#include 

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 tmp(new int(i));
    // std::shared_ptr tmp(std::make_shared(i));
    // std::unique_ptr tmp(new int(i));
    // std::unique_ptr tmp(std::make_unique(i));
  }

  std::chrono::duration 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. Получаются такие числа:
image


Две вариации std::unique_ptr на Linux и Windows показывают такую же производительность, как обычные указатели. За деталями этого теста обратитесь к моей прошлой статье: Потребление памяти и производительность умных указателей.


Семантика владения

Честно говоря, мы используем указатели и, в частности, обычные указатели очень часто. Вопрос, должны ли вы использовать указатель, сводится к следующему: Кто владелец? К счастью, с помощью кода мы можем чётко выразить это.


  • Локальные объекты. Рантайм C++ как владелец автоматически управляет жизнью таких ресурсов. То же самое относится к глобальным объектам или членам класса. Справочники сводят это к области видимости.
  • Ссылки: я не владелец. Я только обеспечиваю, что ресурс не может быть пустым.
  • Обычные указатели: я не владелец. Я только ссылаюсь на ресурс, если он есть. Я не должен удалять ресурс.
  • std: unique_ptr: я исключительный владелец ресурса. Я могу явно освободить мой ресурс.
  • std: shared_ptr: я разделяю ресурс с другими std::shared_ptr. Я могу явно удалить мой разделяемый ресурс, если он больше никому не нужен.
  • std: weak_ptr: я не владелец ресурса, но я могу временно разделять ресурс при вызове моего метода std::weak_ptr::lock.


Нам нужно будет изменить только одну из шести практик использования указателей и мы рады следующему шагу в развитии C++.

© Habrahabr.ru