While working on a project, I came across an interesting problem when passing an object into another object through its constructor when the object passed in is guaranteed to outlive (in terms of memory lifetime) the recipient object. Please bare in mind that I am still learning the ins-and-outs of C++11/C++14, so I am looking for constructive discussion that will help in my understanding of memory management and lifetimes with C++11/C++14-style semantics.
The setup for this question is as follows:
class TopLevelClass {
public:
void someMethod (int someValue) {
// Do some work
}
std::unique_ptr<Context> getContext () {
return std::make_unique<Context>(this);
}
};
class Context {
public:
Context (TopLevelClass* tlc) : _tlc(tlc) {}
void call (int value) {
// Perform some work and then call the top level class...
_tlc->someMethod(value);
}
protected:
TopLevelClass* _tlc;
};
Although a valid alternative to this setup would be to pass the TopLevelClass as an argument into the call method of the Context class, this is not possible in the scenario I am illustrating: The client code with access to a Context object may not have access to the TopLevelClass object.
While the code illustrated above is functionality correct for my needs, I feel as though there exists a code smell. Namely, storing a handle to the TopLevelClass object as a raw pointer does not convey the fact that the Context class is not responsible for managing the lifetime of this pointer (since, in this case, the TopLevelClass is guaranteed to outlive any Context object). Additionally, with use of C++11, I am hesitant to use a raw pointer, rather than a smart pointer (as per Scott Meyer's suggestion in Effective Modern C++).
One alternative I explored is to pass in the handle to the TopLevelClass using a shared pointer and storing this handle within the Context class as a shared pointer. This requires that the TopLevelClass inherit from std::enabled_shared_from_this in the following manner:
class TopLevelClass : public std::enable_shared_from_this<TopLevelClass> {
public:
// Same "someMethod(int)" as before...
std::unique_ptr<Context> getContext () {
return std::make_unique<Context>(shared_from_this());
}
};
class Context {
public:
Context (std::shared_ptr<TopLevelClass> tlc) : _tlc(tlc) {}
// Same "call(int)" as before...
protected:
std::shared_ptr<TopLevelClass> _tlc;
};
The downside to this approach is that unless a std::shared_ptr exists for TopLevelClass a priori, then a std::bad_weak_ptr exception will be thrown (for more information, see this post). Since, in my case, there is no std::shared_ptr<TopLevelClass> created in the code, I cannot use the std::enable_shared_from_this<T> approach: I am restricted to returning a single instance of TopLevelClass using a static raw pointer, as per the requirements of my project, as follows
static TopLevelClass* getTopLevelClass () {
return new TopLevelClass();
}
Is there an approach that exists that conveys the fact that Context is not responsible for managing its handle to the TopLevelClass instance, since the TopLevelClass will be guaranteed to outlive any Context object? I am also open to suggestions about changing the design that will side-skirt the problem altogether, so long as the design change does not overly complicate the simplicity of the design above (i.e., creating many different classes in order to get around simply passing a single pointer into the constructor of Context).
Thank you for your help.