Reputation: 3525
I have a not-so-ideal situation where a class returns handle references to objects that shouldn't be accessed after the parent objects' lifetimes. What is the best way to alter the pattern below to aid defensive coding?
// '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's 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();
}
I know you can't truly prevent a user of C++ from doing most unsafe things, but if I could at least generate warnings on trivial accidental uses like this is would be the bulk of what I'd like to achieve.
Upvotes: 0
Views: 89
Reputation: 391
I'm taking the liberty of making a few assumptions about your situation:
A
at the users discretion.A
is out of scope, thus A
cannot be used as a mandatory gateway.A
is destroyed, thus automatic garbage collection cannot be applied to the handles.With that in mind here's a possible solution:
Within A
's constructor, allocate some kind of signal object S
which is set when A
is destroyed. Handle S
using a shared_ptr
. Have A::handle
return a custom handle class H
which contains a B
handle, and a shared_ptr
to S
. Create a dereference operator within H
which verifies that A
is still valid (S
is not set), or throws an exception. When all handles expire, S
will be destroyed automatically.
Upvotes: 1
Reputation: 897
You want object A to produce another object of class B, let somebody use it, then ensure that B is destroyed before A?
Rather than return an instance of B, would it be possible to define a method on A which obtains the B and then passes it to some kind of delegate (virtual method, functor, lambda function)? This way the user function is nested inside a call to a method on the A object, so it's logically impossible for A to be destroyed before the user code has finished whatever it is doing.
For instance:
class A
{
public:
template <typename Functor>
void DoSomethingWithAnInstanceOfB(const char* whichB, Functor f)
{
B& bref = InternalLookupB(whichB);
f(bref);
}
};
This looks up the correct B instance and then passes it to an arbitrary functor. The functor can do whatever it wants, but it necessarily must return before DoSomethingWithAnInstanceOfB() will return, therefore guaranteeing that A's lifetime is at least as long as B.
Upvotes: 0