Question on boost::shared_ptr

Started by
14 comments, last by blackfe2010 11 years, 5 months ago

So you means two or more shared_ptr can not point to the same raw pointer?
[/quote]
Two shared pointers can share the same object. However, the way to do this is to create one shared_ptr<> to the object, and then copy the shared_ptr<>:

Example *example = new Example();
boost::shared_ptr<Example> one(example);
boost::shared_ptr<Example> two = one;

Under the hood, this means that both shared_ptrs are aware of one another and the reference count of the object will be 2.

If you wite code like this:

Example *example = new Example();
boost::shared_ptr<Example> one(example);
boost::shared_ptr<Example> two(example);

Then each shared_ptr assumes that it has exclusive ownership of the object. There will be two reference counts, both set to 1. When "one" goes out of scope, it will delete the object (provided no additional shared_ptr instances are created from it). Now two is left with a dangling pointer, and when it goes out of scope it will attempt to delete an invalid pointer.


And actually, I want do something like ... So it is incorrect?
[/quote]
You appear to have a circular reference, so none of these shared_ptr instances will ever be destroyed. While you can break the cycle using weak_ptr<> or a raw pointer, I would advise you to also consider alternative designs that are less coupled to one another. For example, why does the Component class need to talk to the "Object Manager"? I would question the necessity of an "Object Manager" class.
Advertisement

why does the Component class need to talk to the "Object Manager"? I would question the necessity of an "Object Manager" class.


Because i want Component can communicate to other Components

class Component
{
public:
void sendMessage(string str)
{
m_objectManager->sendGlobalMessage(str);
}
};
class ObjectManager
{
pubic:
void sendGlobalMessage(string str)
{
for_each(m_allComponents.begin(),m_allComponents.end(),...);
}
};


And about the weak_ptr<>, you means i should do something like:

class Component
{
boost::weak_ptr<ObjectManager> m_ojbectManager;
};

right?
but weak_ptr haven't operator -> how can i use it?
You have to lock a weak pointer which turns it into a shared pointer for the duration of the scope. For example std::shared_ptr<ObjectManager> manager = m_objectManager.lock();

I want do something like this:

class Manager
{
public:
void addComponent(boost::shared_ptr<Component> comp)
{
comp->m_Manager=this;
}
};
class Component
{
public
boost::shared_ptr<Manager> m_Manager;
};



In such circumstances, since 'Component' does not "own" (unique_ptr) or "share ownership" (shared_ptr) of the lifetime of Manager, Component should use a raw pointer. Raw pointers are not evil. Smart pointers don't replace raw pointers in every circumstance, only the circumstances where the pointer needs to manage the lifetime of the memory. In your example, you are just accessing the memory.

If it is possible for Manager to be destructed before the components are destructed, then you should use a weak_ptr (if Manager is managed by a smart_ptr) - otherwise, if Manager's lifetime is guaranteed to exceed every Component lifetime, raw pointer is the proper solution. When adding the new tool "smart pointer" to your toolbox, you don't discard the previous tool "raw pointer", even if 90% of the old tool's duty is now taken over by the new tool. You don't throw away your screwdrivers when you buy a drill - there are many situations where a screwdriver is superior. smile.png
([size=2]I have owned many a drill and many a screwdriver and frequently use them both - different tools for different jobs, even if their potential usage overlap 90% of the time)


class Manager
{
public:
void addComponent(boost::shared_ptr<Component> comp)
{
comp->m_Manager = this;
}
};
class Component
{
private:
friend class Manager;
Manager* m_Manager;
};


shared_ptr is (most likely) the proper choice for Manager's tracking of the Components (I'm assuming Manager has a vector of shared_ptr<Component>?). However, why unnecessarily copy the shared_ptr? Pass it to addComponent as a const ref.
void addComponent(const boost::shared_ptr<Component> &comp)
{
this->components.push_back(comp);
this->components.back()->m_Manager = this;
}


Further, typedefs are awesome.
class Component
{
//I use wPtr for 'weak pointer', uPtr for 'unique ptr', and I personally use 'Ptr' for shared_ptr, but sPtr would also be good.
//I don't typedef raw pointers, as the asterisk itself identifies it better than any single letter could: Component *ptr;
typedef boost::shared_ptr<Component> sPtr;
};

Component::sPtr mySharedPtr;


Furtherly further, one of the main points of smart pointers is that you don't need to handle the destruction of your data. A common rule of C++, is "Never new without delete", "never new[] without delete[]", "never malloc() without free()", etc...

Using smart pointers, while yes, technically you are new-ing and delete-ing properly, more accurately, you are new-ing, and the smart pointer is delete-ing. It'd be better (though not required), if you let smart pointers do the new-ing and delete-ing for you, so that a single API handles it entirely for the sake of consistency.

Enter boost::make_shared. More typing, but logically better as you ensure there is no chance for you to possibly mishandle the memory.
boost::make_shared() new()s the data, and returns it already in a shared_ptr, so you don't accidentally make the two deadly mistakes (both which you already made in this thread):
1) Passing stack data to a shared_ptr, mistakenly telling shared_ptr is is solely responsible for deleting the data (which isn't true, the stack would delete it)
2) Passing dynamic memory to two separate shared_ptrs. mistakenly telling each shared_ptr individually and separately that it is solely responsible for deleting the data (which isn't true, they both are responsible, but you accidentally told them they they were solely responsible).

Both avoided if you hold yourself to a single simple rule: Always use make_shared() to create your shared_ptrs. smile.png
Component::sPtr mySharedPtr = boost::make_shared<Component>(/* Component's constructor arguments can go here, as if this was a normal constructor */);

std::unique_ptr = I own this object, nobody else owns it _or_ uses it.
std::shared_ptr = I own this object, possibly with shared ownership (through other std::shared_ptrs), and possibly with shared use (through weak_ptrs).
std::weak_ptr = I don't own this object, I just use it.
Regular raw pointer '*' = I don't own this object, I just use it (only use if the pointer's lifetime is guaranteed to be longer than the class containing it)

([size=2]The smart pointers in boost are probably in std::tr1:: namespace if you are not using C++11, or in std:: namespace if you are using C++11. They are basically the same, though they may have some minor changes. Boost is often a testing ground for new C++ Standard Library features)

weak_ptr can only be created from shared_ptrs. Weak pointers to memory not managed by a shared_ptr would basically be a raw pointer anyway ([size=2]talking about high level, not implementation details), if it was allowed - which thankfully it isn't.

Enabling shared_from_this, is most likely not the correct thing you want to be using here (but it's hard to tell from only a small portion of code).
Using weak_ptrs to the Manager is also most likely the wrong choice.

Raw pointers are not evil, and this is one of the situations where they are the better solution.
make_shared() has another advantage, it allows the implementation to do a single allocation for the object data and the reference counting data, rather than having two allocations.
Thank you for your help!!!

I think it's clear for me.

This topic is closed to new replies.

Advertisement