Опасен ли C++ shared_ptr :: operator *?

  shared_ptr<int> ptr = make_shared<int>(10);
  int& val = *ptr;
  ptr = make_shared<int>(20);
  // val now points to freed memory

В приведенном выше коде я могу читать и записывать в val, который указывает на освобожденную память. Та же проблема возникает, если мы используем .get () в shared_ptr. Таким образом, можно прострелить себе ногу, даже если прибегнуть к умным указателям.

Очевидно, что никто не будет кодировать так, как указано выше. Один из способов добиться этого - если у нас будет что-то вроде этого -

class Foo {
public:
   int& getVal() { return *p; }
private:
   shared_ptr<int> p;
};

Кто-то может вызвать getVal (), после чего другой член вышеуказанного класса может перезаписать p другим значением. Если getVal () выше возвращает shared_ptr вместо ссылки, мы не увидим этой проблемы. Некоторые могут возразить, что возврат shared_ptr дороже, чем возврат ссылки, поскольку нам нужно увеличить счетчик в блоке управления shared_ptr.

Итак, следует ли руководствоваться тем, чтобы не возвращать ссылку на shared_ptr, как указано выше?

Вы также можете вернуть std::weak_ptr

Fureeish 25.10.2018 20:56

В С ++ все опасно при неправильном использовании ...

Alan Birtles 25.10.2018 21:12

И действительно, проблема в висящих отсылках. Это не имеет ничего общего с shared_ptr как таковым.

Peter Ruderman 25.10.2018 21:13

C++ - это не язык для няни. Вам дают много веревки, чтобы прострелить себе ногу. И const - ваш друг.

Eljay 25.10.2018 21:14
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
131
1

Ответы 1

Опасность здесь не в operator*, а в хранении ссылок.

Сохранение ссылок на что-нибудь означает, что вы несете личную ответственность за срок службы объекта, о котором говорите.

int getVal() { return *p; }

это самое безопасное и простое решение. C++ обожает ценности.

У вас есть более сложный объект, копирование которого стоит дорого? Тогда будьте внимательнее.

gsl::span<int const> getVals() { return {p->data(), p->size()}; }

здесь у нас есть что-то, что является ссылкой концептуально, несмотря на то, что она не относится к типу ссылки или указателя.

Потребители должны знать правило срока службы того, что возвращает getVals. Если они хотят сохранить это, они несут ответственность за копирование данных.

Только в крайних случаях следует учитывать:

std::shared_ptr<gsl::span<int const>> getVals() {
  return p?make_shared_span<int const>( p, {p->data(), p->size()} ):nullptr;
}

где make_shared_span создает общий вид диапазона в общий ptr.

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


Один из способов обойти проблемы «совместного использования ссылок» и времени жизни в базах кода C++ - это работа с неизменяемыми данными.

shared_ptr<gsl::span<int const>const> с на самом деле неизменный - гораздо более разумная вещь для работы, чем shared_ptr<gsl::span<int>>.

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


«Решение» этой проблемы путем передачи общих указателей на данные повсюду приведет к тому, что объекты будут жить намного дольше, чем следовало бы, поскольку люди хранят некоторый общий указатель на них, никогда не намереваясь использовать его снова.

Обратите внимание на срок службы, и вы получите прекрасные результаты RAII.

Бывают ситуации, когда совместное использование общего указателя имеет смысл, и это когда вы на самом деле хочу совместное владение, а не когда «я не хочу думать о времени жизни». Совместное владение - это более сложный вид жизни, потому что в некоторых ситуациях вам не нужно думать о результатах жизни в спагетти на всю жизнь.

Хранить ссылки опасно. Можем ли мы просто вытатуировать это на внутренней стороне век программистов на C++?

Nicol Bolas 25.10.2018 20:59

Другие вопросы по теме