Является ли возврат ссылки на управляемую память злоупотреблением служебным положением?

Учитывая этот код:

class Clazz {
 private:
  std::shared_ptr<int> something;

 public:
  Clazz() : something(std::make_shared<int>(0)) {}

  int &getSomething() {
    return *something;
  }
}

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

Ссылка не висит, пока объект Clazz жив (и something не сброшен). Таким образом, могут быть сценарии, в которых это действительно. Почему вы хотите это сделать, это другой вопрос. Вы не предоставили нам достаточно контекста, чтобы давать советы.

wohlstad 18.07.2024 18:00

Давайте не будем делать никаких предположений о сроке службы объекта Clazz.

tjzel 18.07.2024 18:02

В стандартной библиотеке есть много случаев, когда возвращается ссылка на память, принадлежащую классу. Например, std::vector::operator[], а также std::shared_ptr::operator*, который вы здесь использовали.

interjay 18.07.2024 18:04

Если объект Clazz умирает, ссылка действительно может висеть (но, поскольку это shared_ptr, могут быть другие общие владельцы, поддерживающие something живым).

wohlstad 18.07.2024 18:04

Суть в том, что ссылка действительна до тех пор, пока жив объект, управляемый shared_ptr.

wohlstad 18.07.2024 18:06

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

Wutz 18.07.2024 18:16

У Rust есть время жизни в этом типе, а у C++ — нет.

Jarod42 18.07.2024 18:39

@tjzel Время жизни указанного int частично зависит от времени жизни связанного Clazz экземпляра. Если мы не можем делать никаких предположений о времени жизни объекта Clazz и не располагаем дополнительной информацией о других экземплярах Shared_ptr для этого int, то мы не сможем ничего ответить о достоверности ссылки.

François Andrieux 18.07.2024 19:15

Если вы используете std::shared_ptr, передайте его по значению. В этом весь смысл. Возвращайтесь по ссылке только в том случае, если вы можете гарантировать, что рассматриваемый объект не исчезнет из-под вас.

catnip 18.07.2024 23:31
Стоит ли изучать 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
9
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Время жизни «необработанных» указателей всегда должно быть явным.

Все просто, если у вас есть умный указатель и вы предоставляете своим внутренним функциям необработанные указатели (или ссылки) — вам просто нужно помнить, что эти указатели нигде не следует хранить. А поскольку вы держите умный указатель, все функции, которые вы вызываете с помощью необработанного указателя, не будут иметь проблем.

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

/* ...
 * has<T> returns a pointer to the stored item, or nullptr.  This
 * pointer is valid until a yield.
 */

чтобы напомнить людям и/или себе, что нельзя держать этот указатель под рукой.

В других случаях я мог бы возвращать указатели на внутренние буферы, сообщая людям, что данные не изменятся до следующего вызова.

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

Также обратите внимание, что в моей книге необработанные указатели имеют допустимую силу, но не имеют связанной с ними функции free. Они просто становятся недействительными в определенный момент времени, но пользователям никогда не придется их освобождать — я всегда возвращаю что-то более продвинутое, если такие вещи нужно очистить.

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