Является ли C ++ shared_ptr :: operator * опасным? - PullRequest
0 голосов
/ 25 октября 2018
  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, как описано выше?

1 Ответ

0 голосов
/ 25 октября 2018

Здесь опасность не 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 создает общий вид пролета вобщий ресурс.

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


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

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

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


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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...