У меня есть не столь идеальная ситуация, когда класс возвращает ссылки на ссылки на объекты, к которым не следует обращаться после жизни родительских объектов. Каков наилучший способ изменить шаблон ниже, чтобы помочь защитное кодирование?
// '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++ совершать самые небезопасные вещи, но если бы я мог, по крайней мере, генерировать предупреждения о тривиальных случайных применениях, как это было бы основной частью того, что я хотел бы достичь.
Я беру на себя смелость сделать несколько предположений о вашей ситуации:
A
по усмотрению пользователя.A
выходит за пределы области видимости, таким образом, A
не может использоваться как обязательный шлюз.A
, поэтому автоматическая сборка мусора не может быть применена к ручкам.Имея это в виду, это возможное решение:
В A
конструктора, выделить какой - то объект сигнала S
, который установлен, когда разрушается. A
Ручка S
с помощью shared_ptr
. Пусть A::handle
возвращает пользовательский класс классов H
который содержит дескриптор B
, а shared_ptr
- S
Создайте оператор Dereference внутри H
который проверяет, что A
все еще действителен (S
не установлен), или генерирует исключение. Когда все ручки истекают, S
будет автоматически уничтожен.
Вы хотите, чтобы объект 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.
A<>
основном действует как фабрика для асинхронных получателей, поэтому базовые объекты нельзя безопасно изменять или даже постоянно читать после того, как владение переходит в вызов ~A()
. A<T>
не может расширять T
так как сами Ts
не могут жить в стеке, и создание большого количества функторов / функций typedefs нереально, так как оставление всех управляемых T
классов в покое и без помех является высоким приоритетом. Я ценю ваше предложение, хотя, даже если я не могу использовать его в этом случае.
handle()
в указателях или ссылках. Либо используйте его напрямую (например,a.handle().methodOfB()
либо скопируйте егоB b=a.handle()
(при условии, что B имеет правильную семантику копирования)a.handle.methodOfB()
определенно возможно, но я бы хотел добавить обнаружение во время сборки сохранения ссылки с более длинным временем жизни, чем у оболочки, чтобы сделать использование этого заведомо сложного интерфейса немного сложнее.