When potentially manipulating an object it is sometimes good to keep track of the previous object. In object-oriented programming that means constructing a new object with a pointer to this
inside the virtual function.
Clearly that is problematic in terms of memory management, and specifically for std::shared_ptr
.
One solution is to derive from std::enable_shared_from_this
and use shared_from_this
see how to return shared_ptr to current object from inside the "this" object itself
However, to be safe we need to also hide the constructor to enforce that construction uses new, Visitor Pattern and std::shared_ptr
But this means we lose performance since we cannot directly use std::make_shared
, and we don't get the same benefit automatically - Does enable_shared_from_this and make_shared provide the same optimization
That can be circumvented, but the per-class boiler-plate currently seems excessive: How do I call ::std::make_shared on a class with only protected or private constructors?
Note: below there is just one class B - but in practice there might be a large number of them and several foo-operations (doing similar but related things on similar and related objects). That seems to differ from some of the previous questions.
struct A : public std::enable_shared_from_this<A> {
virtual ~A() { ; }
std::shared_ptr<A> pred; // Previous A-object
virtual std::shared_ptr<A> foo() { return shared_from_this(); };
// Possibly return a pointer to a new A
protected:
A() = default;
};
struct B : public A {
template<typename ...args>
static auto create(args&& ... params) {
return std::shared_ptr<B>(new B(std::forward<args>(params)...));
};
int x = 0;
virtual std::shared_ptr<A> foo() {
std::shared_ptr<A> res = B::create(x + 1);
res->pred=shared_from_this();
return res;
}
private:
B(int x_) : x{ x_ } {; }
};
Another solution would be to have a helper function taking the shared_ptr
. That works, but all calls would need to use A::foo(x)
instead of x->foo()
which looks somewhat odd. However, the boiler-plate is in this case common for A::foo
so it can be shared between different derived classes.
struct A {
virtual ~A() {;}
std::shared_ptr<A> pred; // Previous A-object
static std::shared_ptr<A> foo(std::shared_ptr<A>&f) {
std::shared_ptr<A> result;
if (f) result=f->fooInternal();
if (result) result->pred=f; // Set predecessor.
else result=f; // Return original if nothing happened.
return result;
}
protected:
virtual std::shared_ptr<A> fooInternal() {return nullptr;};
// Possibly return a pointer to a new A
// Return nullptr to signify that nothing happened
};
struct B : public A {
int x=0;
B(int x_) :x{ x_ } { ; }
virtual std::shared_ptr<A> fooInternal() {
return std::make_shared<B>(x+1);
}
};
Are there any caveats with these, and/or are there any other better alternatives?