C ++ хинтинг / предупреждение о возвращенных ссылочных временах жизни

0

У меня есть не столь идеальная ситуация, когда класс возвращает ссылки на ссылки на объекты, к которым не следует обращаться после жизни родительских объектов. Каков наилучший способ изменить шаблон ниже, чтобы помочь защитное кодирование?

// 'A<T>' is a thin factory-style utility class with asynchronous consumers.
template <typename T>
struct A {
  A() : h_(*(new T())) { /* ... */ }
  ~A() { /* h_ deleted or ownership passed elsewhere */ }

  // What the best way to indicate that these handles shouldn't be used after
  // the destructions of the A instances?
  T &handle() { return h_; }

private
  T &h_;
};

struct B { /* ... */ };

int main() {
   B *b1{nullptr};
   {
     A<B> a;

     // Is there a good way to trigger detection that the reference is bound to
     // a variable which will outlive its 'valid' local lifetime?
     b1 = &a.handle();

     B &b2(a.handle()); // this is reasonable though

     b1->ok_action();
     b2.also_alright();
   }
   b1->uh_oh();
}

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

  • 0
    Я понимаю, что это существующий код, но я не уверен, что вы должны сохранить и что вы можете изменить. Я бы сказал, что вы должны прекратить сохранять результат handle() в указателях или ссылках. Либо используйте его напрямую (например, a.handle().methodOfB() либо скопируйте его B b=a.handle() (при условии, что B имеет правильную семантику копирования)
  • 0
    @jsantander A <> - это своего рода оболочка менеджера времени жизни для объектов, которые будут обворованы в момент уничтожения оболочки, поэтому семантика копирования, к сожалению, не применяется. Всегда вызывать a.handle.methodOfB() определенно возможно, но я бы хотел добавить обнаружение во время сборки сохранения ссылки с более длинным временем жизни, чем у оболочки, чтобы сделать использование этого заведомо сложного интерфейса немного сложнее.
Показать ещё 1 комментарий
Теги:
object-lifetime

2 ответа

1

Я беру на себя смелость сделать несколько предположений о вашей ситуации:

  • Ручки указывают на динамически выделенные объекты, созданные A по усмотрению пользователя.
  • Ручки будут передаваться туда, где A выходит за пределы области видимости, таким образом, A не может использоваться как обязательный шлюз.
  • Данные, для которых точка дескриптора должна быть уничтожена при уничтожении A, поэтому автоматическая сборка мусора не может быть применена к ручкам.
  • Пока что проверка безопасности во время компиляции не представляется возможной. Вы хотите, чтобы ошибки кодирования проявлялись во время выполнения через какой-то механизм исключения, а не спонтанные сбои.

Имея это в виду, это возможное решение:

В A конструктора, выделить какой - то объект сигнала S, который установлен, когда разрушается. A Ручка S с помощью shared_ptr. Пусть A::handle возвращает пользовательский класс классов H который содержит дескриптор B, а shared_ptr - S Создайте оператор Dereference внутри H который проверяет, что A все еще действителен (S не установлен), или генерирует исключение. Когда все ручки истекают, S будет автоматически уничтожен.

0

Вы хотите, чтобы объект A создавал другой объект класса B, позволял кому-то его использовать, а затем убедитесь, что B уничтожен до A?

Вместо того, чтобы возвращать экземпляр B, можно ли определить метод на A, который получает B, а затем передает его некоему делегату (виртуальный метод, функтор, лямбда-функция)? Таким образом, пользовательская функция вложена внутри вызова метода на объект A, поэтому логически невозможно, чтобы A был уничтожен до того, как код пользователя завершил все, что он делает.

Например:

class A { public: template <typename Functor> void DoSomethingWithAnInstanceOfB(const char* whichB, Functor f) { B& bref = InternalLookupB(whichB); f(bref); } };

Это ищет правильный экземпляр B, а затем передает его произвольному функтору. Функтор может делать все, что захочет, но обязательно должен вернуться, прежде чем DoSomethingWithAnInstanceOfB() вернется, поэтому гарантируя, что время жизни A не меньше, чем B.

  • 0
    К сожалению, динамический поиск для этого класса шаблона управления не представляется возможным. A<> основном действует как фабрика для асинхронных получателей, поэтому базовые объекты нельзя безопасно изменять или даже постоянно читать после того, как владение переходит в вызов ~A() . A<T> не может расширять T так как сами Ts не могут жить в стеке, и создание большого количества функторов / функций typedefs нереально, так как оставление всех управляемых T классов в покое и без помех является высоким приоритетом. Я ценю ваше предложение, хотя, даже если я не могу использовать его в этом случае.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню