1

I want to have the following class structure:

#include <tr1/memory>

class Interface;
class Impl;

class Impl
{
public:
    Impl( std::tr1::weak_ptr< Interface > interface );

private:
    std::tr1::weak_ptr< Interface > interface_;
};

class Interface
{
public:
    Interface() { impl_ = new Impl( this ); }

private:
    std::tr1::shared_ptr< Impl > impl_;
};

Impl::Impl( std::tr1::weak_ptr< Interface > interface )
        : interface_(interface)
{}

The code doesn't work since a weak_ptr can only be constructed from a shared_ptr. I can't construct a shared_ptr of this in the ctor since it would destroy the object when leaving the ctor.

The interface will be held as a shared_ptr by the caller. The Implementation needs to be shared_ptr since its lifetime is longer than the Interface lifetime.

Is there an elegant way to establish this relationship?

eile
  • 1,153
  • 6
  • 18
  • 1
    Can you make the constructor of `Interface` private to force creation in some other means (like from a factory) that will only over create `Interface` as a shared_ptr? – Chad Jul 14 '11 at 13:18
  • That is the one non-elegant way I found so far. – eile Jul 14 '11 at 13:22
  • The relationship itself is hideous, so I'm perfectly happy to suggest that you're trying to do something inelegant and the likely solution is that it will be inelegant. – Puppy Jul 14 '11 at 13:32
  • Can you describe what problem you are actually trying to solve with this construction? – Eelke Jul 14 '11 at 13:38

2 Answers2

0

If I've understood your question correctly, I think you can use std::tr1::enable_shared_from_this to achieve the effect you want:

#include <tr1/memory>

class Interface;
class Impl;

class Impl
{
public:
    Impl( const std::tr1::weak_ptr< Interface >& interface );

private:
    const std::tr1::weak_ptr< Interface > interface_;
};

class Interface : public std::tr1::enable_shared_from_this<Interface>
{
public:
    Interface();
    std::tr1::shared_ptr< Impl > getImpl();

private:
    std::tr1::shared_ptr< Impl > impl_;
};

inline Interface::Interface()
{
}
std::tr1::shared_ptr< Impl > Interface::getImpl()
{
    if ( !impl_ ) {
        impl_ = std::tr1::shared_ptr< Impl >( new Impl( shared_from_this() ) );
    }
    return impl_;
}

inline Impl::Impl( const std::tr1::weak_ptr< Interface >& interface )
        : interface_(interface)
{}


int main(int argc, char* argv[])
{
    std::tr1::shared_ptr< Interface >  x(new Interface());
    return 0;
}

The relationship may not be very nice (as judging by the class names), but the general problem of creating circular structures with smart pointers is valid. The shared pointer to the Interface instance must have have been created before shared_from_this() is called. This means that Interface must be allocated dynamically and handled by a smart pointer, and that shared_from_this() cannot be called from the Interface constructor, so I have added the lazy getter to create the Impl instance.

acm
  • 12,183
  • 5
  • 39
  • 68
  • The problem with this seems to be (from http://stackoverflow.com/questions/712279/what-is-the-usefulness-of-enable-shared-from-this): The shared pointer that this returned will have a different reference count from the "proper" one, and one of them will end up losing and holding a dangling reference when the object is deleted. – eile Jul 14 '11 at 13:33
  • 1
    Yes I agree that is a problem, so the above won't work. That is unfortunate. In that case I think the earlier suggestion to wrap this up in a factory is probably the simplest way forward. – acm Jul 14 '11 at 13:40
0

I solved it by removing the need to use shared_ptr< Interface > from the Impl.

The underlying problem was that the Interface is a Node in a directed acyclic graph. Each node knows its parents and children, so implementing a Node::addChild( shared_ptr< Node > child ) is impossible, since the node cannot be added as a weak_ptr to the child's parents.

One way is to use intrusive_ptr, but I solved it by using a static Node::link( shared_ptr< Node > parent, shared_ptr< Node > child ) method for now.

I might end up using intrusive_ptr later if I need to do a enable_shared_from_this-like operation.

eile
  • 1,153
  • 6
  • 18