tags:

views:

96

answers:

3

I have to register an object in a container upon its creation. Without smart pointers I'd use something like this:

a_class::a_class()
{
    register_somewhere(this);
}

With smart pointers I should use shared_from_this but I can't use that in the constructor.

Is there a clean way to solve this problem? What would you do in a similar situation? I'm thinking about introducing an init method to call just after creation and put everything in a factory function like this:

boost::shared_ptr<a_class> create_a()
{
    boost::shared_ptr<a_class> ptr(new a_class);
    ptr->init();
    return ptr;
}

Is it fine or there is a standard procedure to follow in such cases?

EDIT: Actually my case is more complex. I have 2 object which shall maintain pointers each other. So the truth is I'm not "registering" but creating another object (let's say b_class) which requires this as a parameter. b_class receives this as a weak pointer and stores it.

I'm adding this because since you are giving me design advices (which are very appreciated) at least you can know what I'm doing:

a_class::a_class()
{
    b = new b_class(this);
}

In my program a_class is an entity and b_class is one of the concrete classes representing the state (in the constructor it's just the starting state). a_class needs a pointer to the current state and b_class needs to manipulate the entity.

a_class is responsible for creating and destroying b_class instances and thus maintains a shared_ptr to them but b_class need to manipulate a_class and thus maintains a weak pointer. a_class instance "survives" b_class instances.

Do you suggest to avoid using smart pointers in this case?

+2  A: 

If you absolutely need a shared_ptr during construction, it's best to have an 'init' function. In fact, this is the only decent approach I can think of. You should probably have a special function that creates objects of this type, to ensure init() is called, if you choose this path.

However, depending on what you're registering for, it may be a better idea to give whatever object you're registering with a plain pointer to the object in the constructor, rather than a shared_ptr. Then in the destructor, you can just unregister the object from the manager.

dauphic
A: 

Why don't you use http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/enable_shared_from_this.html

struct a_class : enable_shared_from_this<a_class> {
    a_class() {
        shared_ptr<a_class> ptr(this);
        register_somewhere(ptr);
    }
};

Update: here is a complete working example:

#include <stdio.h>
#include <boost/smart_ptr/enable_shared_from_this.hpp>

struct a_class;
boost::shared_ptr<a_class> pa;

void register_somewhere(boost::shared_ptr<a_class> p)
{
    pa = p;
};

struct a_class : boost::enable_shared_from_this<a_class> {
private:
    a_class() {
        printf("%s\n", __PRETTY_FUNCTION__);
        boost::shared_ptr<a_class> ptr(this);
        register_somewhere(ptr);
    }

public:
    ~a_class() {
        printf("%s\n", __PRETTY_FUNCTION__);
    }

    static boost::shared_ptr<a_class> create()
    {
        return (new a_class)->shared_from_this();
    }
};

int main()
{
    boost::shared_ptr<a_class> p(a_class::create());
}

Note the factory function a_class::create(). Its job is to make sure that only one reference counter gets created. Because

boost::shared_ptr<a_class> p(new a_class);

Results in creation of two reference counters and double deletion of the object.

Maxim Yegorushkin
I don't think this example is working: you may end having 2 different counters for the same pointer...
happy_emi
Posted a complete working example.
Maxim Yegorushkin
the correct header to include is `<boost/enable_shared_from_this.hpp>` not `<boost/smart_ptr/enable_shared_from_this.hpp>`
Sam Miller
+1  A: 

a_class is responsible for creating and destroying b_class instances

...

a_class instance "survives" b_class instances.

Given these two facts, there should be no danger that a b_class instance can attempt to access an a_class instance after the a_class instance has been destroyed as the a_class instance is responsible for destroying the b_class instances.

b_class can just hold a pointer to it's associated a_class instance. A raw pointer doesn't express any ownership which is appropriate for this case.

In this example it doesn't matter how the a_class is created, dynamically, part of a aggregated object, etc. Whatever creates a_class manages its lifetime just as a_class manages the lifetime of the b_class which it instantiates.

E.g.

class a_class;

class b_class
{
public:
    b_class( a_class* a_ ) : a( a_ ) {}
private:
    a_class* a;
};

class a_class
{
public:
    a_class() : b( new b_class(this) ) {}
private:
    boost::shared_ptr<b_class> b;
};

Note, in this toy example there is no need for a shared_ptr, an object member would work just as well (assuming that you don't copy your entity class).

class a_class
{
public:
    a_class() : b( this ) {}
private:
    b_class b;
};
Charles Bailey