Reputation: 1277
I have situation similar to included:
class A
{
public:
A(shared_ptr<B>);
}
class B : public enable_shared_from_this<B>
{
const shared_ptr<A> a;
}
I can't have shared_ptr
to B
before construction, so before a
is initialized. So, I need to initialize my constant field after construction (I think it denies RAII), or just construct it later (so it can't be const, so it denies const-correctness, and also looks like not-too-consistent with RAII).
It looks like propably common situation. Is there any the cleanest way to handle this? How would you do this?
Upvotes: 2
Views: 328
Reputation: 171303
If A
doesn't actually keep a copy of the shared_ptr<B>
you pass it (just uses the B
briefly) then you can make this work (see below) but:
A
doesn't keep a reference to the B
then it could just take a B&
or B*
argument, not a shared_ptr<B>
, so you should change the design.A
does keep a reference then you're going to have a circular reference, so you should change the design.This works, but is really, really horrible and would be easy to introduce bugs, and is generally a bad idea, I probably shouldn't even be showing it, just change your design to avoid circular dependencies:
#include <memory>
#include <iostream>
class B;
class A
{
public:
A(std::shared_ptr<B> b);
};
class B : public std::enable_shared_from_this<B>
{
// return a shared_ptr<B> that owns `this` but with a
// null deleter. This does not share ownership with
// the result of shared_from_this(), but is usable
// in the B::B constructor.
std::shared_ptr<B> non_owning_shared_from_this()
{
struct null_deleter {
void operator()(void*) const { }
};
return std::shared_ptr<B>(this, null_deleter());
}
public:
B(int id)
: m_id(id), a(std::make_shared<A>(non_owning_shared_from_this()))
{ }
int id() const { return m_id; }
private:
int m_id;
const std::shared_ptr<A> a;
};
A::A(std::shared_ptr<B> b)
{
std::cout << b->id() << std::endl;
}
int main()
{
auto b = std::make_shared<B>(42);
}
Upvotes: 0
Reputation: 3419
Such a situation is a good indicator to refactor your code. Think about whether B
should actually inhert from A
or be a member of A
before finding a way around this problem...
.. because it is probably going to be to remove the constness of your object - and probably not use shared_ptr (you have a cyclical reference there, so ref-counting alone will never be able to destroy your objects!).
Upvotes: 2
Reputation: 171127
I would solve this by not having const
members, plain and simple. They are generally much more trouble than they're worth (they make the class non-assignable, not even move-assignable, for example).
a
is private, so only the class itself can access it. Thus it should be enough to document "a
should never be modified after being initialised!!!". If you fear that won't be enough (or the class has friend
s outside your control), you can make this even more obvious like this:
class B : public enable_shared_from_this<B>
{
const std::shared_ptr<A>& a() { return _use_this_ONLY_for_initialising_a; }
std::shared_ptr<A> _use_this_ONLY_for_initialising_a;
};
Upvotes: 2