We have a function that returns a new allocated object as a output argument (ref to pointer).
MyFunc(MyObject*& obj)
{
obj = new MyObject();
}
Which is called like so:
Object* obj;
MyFunc(obj);
Internally the function does quite a bit and uses shared_ptr for memory management. When it is done, the object we would like to return is referenced by a shared_ptr. I am struggling on how to return our new allocated object as a regular pointer.
We would like to continue to use shared_ptr internally to reduce risks, but it does not seem to make sense to return a shared_ptr as the caller takes complete ownership over the returned value (the called function or object no longer needs or keeps a reference to the returned data) and we want them to have flexibility.
Does anyone have any suggestions for allowing us to use shared_ptr internally but have a regular pointer interface? Thanks
If for some reason you cannot/want not use std::unique_ptr or std::auto_ptr (for example if you need to have multiple owners internally during creation for some reason or your underlying methods require std::shared_ptr to be passed around), you can still make it work with std::shared_ptr by using custom deleter, as described here: https://stackoverflow.com/a/5995770/1274747
In the principle, after you're done before the return, you switch the deleter to not actually delete the instance (make the deleter "null") and then return by shared_ptr get(). Even after all shared_ptr objects are destroyed, the memory will not be deleted (as the nulled deleter will skip the deletion).
There is also a link in the comments not so well visible, which might be of your interest:
http://paste.ubuntu.com/23866812/
(not sure though if it would really work without the shared ownership of the switch in all cases, would need to test)
EDIT
As expected, with the linked simple disarmable deleter from the pastebin you need to be careful, because the deleter is actually copied for storing in std::shared_ptr.
But you can still make it work by using std::ref:
MyFunc(MyObject*& obj)
{
DisarmableDelete<MyObject> deleter;
std::shared_ptr<MyObject> ptr(new MyObject(), std::ref(deleter));
// do what is necessary to setup the object - protected by deleter
// ...
// disarm before return
deleter._armed = false;
obj = ptr.get();
// deleter disarmed - object not freed
}
And just for completeness (and to avoid a potential future broken link), here is the implementation of DisarmableDelete from http://paste.ubuntu.com/23866812/.
template <typename T, typename Deleter = typename std::default_delete<T> >
struct DisarmableDelete : private Deleter {
void operator()(T* ptr) { if(_armed) Deleter::operator()(ptr); }
bool _armed = true;
};
It depends on who "owns" the pointer, once it has been exposed to the 'outside world.' Ownership essentially boils down to: "who is responsible for freeing this memory, later?"
It can be answered with a simple question: when MyFunc is called, is the caller responsible for deleting the pointer when it's done?
If so, then MyFunc needs to 'release' the ownership, otherwise the shared_ptr will automatically delete the pointer, when it goes out of scope. This actually can't be done, using shared_ptr. You need to use a unique_ptr instead, and call unique_ptr::release().
If not - if MyFunc will simply use the resulting pointer and forget about it without delete-ing it - then you can simply return the 'raw' pointer using shared_ptr::get(). You must be careful, because this implies that the shared_ptr still exists elsewhere in your code.
I can see four alternatives, as highlighted below. They are all horrible, and short of switching your ownership to std::unique_ptr<T> and returning via obj = ptr.release(); I can only offer a hack where the argument is assigned to the pointer upon destruction, but you still need to catch the exception and test whether the pointer was assigned.
#include <iostream>
#include <memory>
#include <exception>
struct foo {
void bar() const { std::cout << this << " foo::bar()\n"; }
~foo() { std::cout << this << " deleted\n"; }
};
void f1(foo*& obj) {
obj = new foo;
// do stuff... if an exception is thrown before we return we are
// left with a memory leak
}
void f2(foo*& obj) {
auto holder = std::make_shared<foo>();
// do stuff.. if an exception is thrown the pointer will be
// correclty deleted.
obj = holder.get(); // awesome, I have a raw pointer!
} // oops, the destructor gets called because holder went out of
// scope... my pointer points to a deleted object.
void f3(foo*& obj) {
auto holder = std::make_unique<foo>();
// do stuff.. if an exception is thrown the pointer will be
// correclty deleted.
obj = holder.release(); // awesome, I have a raw pointer!
} // no problem whem holder goes out of scope because it does not own the pointer
void f4(foo*& obj) {
// a super-weird hack that assigns obj upon deletion
std::shared_ptr<foo> holder(new foo, [&obj](foo*& p){ obj = p; });
throw std::exception();
} // no problem whem holder goes out of scope because it does not own
// the pointer... but if an execption is throw we need to delete obj
int main() {
foo* p1;
f1(p1);
p1->bar();
foo* p2;
f2(p2);
// p2->bar(); // error
foo* p3;
f3(p3);
p3->bar();
foo* p4;
try {
f4(p4);
} catch(...) {
std::cout << "caught an exception... test whether p4 was assigned it\n";
}
p4->bar(); // I still need to delete this thing
}
The point of shared_ptr is to express shared ownership.
Anything that does not share in the ownership of an object -- that doesn't have the right to make an object lifetime last longer, and the object lifetime is the union of the shared owners request for object lifetime -- should not use a shared_ptr.
Here, you have a shared_ptr and you are going to return it. At that point, you are violating the assumptions of shared_ptr; anyone who kept a shared_ptr copy expects that its content can last as long as it requests.
Meanwhile, the calling code thinks it owns the MyObject* raw pointer you passed it.
This is an example of misuse of shared_ptr.
Saying "we have memory management issues, use shared_ptr" doesn't fix memory management issues. Proper use of shared_ptr requires care and design, and the design must be that when the end of the lifetime of the object in question is shared by 2 or more pieces of data and/or code.
The internal code, if it does not own the pointer, should use either something like an observer_ptr<T> or a raw T* (the first to make it clear it doesn't own the object).
Ownership should be explicit, and in a unique_ptr. It can then call .release() to pass ownership to a raw pointer if required; in practice, I would change your signature to take a unique_ptr&, or have it return a unique_ptr.
The caller would then call .release() when they want to use some other object lifetime management system, or that object lifetime management system should consume unique_ptrs (thus being extremely clear about taking ownership of things).
Use a non-hack solution.
Such as a std::shared_ptr<std::unique_ptr<T>>. In this case, you have shared ownership of a unique ownership.
The unique_ptr can have its ownership taken from it (via .release()). When it does so, all of the shared_ptrs that still exist will have their unique_ptr also be cleared.
This places the shared unique ownership front and center, instead of hacking a non-deleter into a shared_ptr and having dangling shared_ptrs that think they have ownership over data but do not.
The best approach is to use internally unique_ptr and call its .release() method before returning the raw pointer.
If you are stuck to use shared_ptr internally, an option is to create it specifying a custom, "noop" deleter, that just does nothing when shared_ptr is destroyed (instead of calling delete on the owned pointer). The get the raw pointer from shared_ptr as usual (.get() method).
An example of such a deleter can be found in the Boost library (null_deleter).
But please note that doing this effectively "disable" the usefulness of having a shared_ptr at all...
If your managing the lifespan of the allocated object internally with std::shared_ptr, and are returning a raw pointer for access and don't want this pointer to affect the ref count, you can return the raw pointer by calling shared_ptr.get().
It can be problematic to return smart pointers if your using a tool like Swig to generate wrappers for other languages.
Related
I am working on wrapping a C++ library into a C bridge.
All objects, I’d like to maintain with shared_ptrs on the heap like:
void* makeFoo() {
return new shared_ptr<void>(shared_ptr::make_shared<Foo>());
}
Can I use a generic destroy like this:
void destroy(void* p) {
delete static_cast<shared_ptr<void>*> p;
}
Or is there a cleaner way?
The type of the argument to delete must match the actual type of the thing you're deleting (otherwise how would the right destructor be invoked, for example?), or at least be a base type in a polymorphic hierarchy so the destructor can be found virtually ([expr.delete]/3).
So, no, you can't do that.
There are a two things at play here:
When you call new SomeType() ... then you need to call delete pointer where pointer has type SomeType * and points to the object allocated by that new expression. There are extensions to this rule with regards to base classes, but inheritance is not involved here, so we'll leave it at that.
shared_ptr<Foo> manages not only the Foo object, but also a "deleter" which knows how to destruct the Foo object. When you construct one shared_ptr from another, then that deleter gets passed on. This allows for "type erasure":
shared_ptr<Foo> typed = make_shared<Foo>();
shared_ptr<void> erased = typed;
Here, erased does no longer have compile time information about the type of the object it points to (that information was "erased"), but still has runtime information (the deleter) about the type of the object.
So to make this work, you need to make sure that you don't violate point 1 above; you need to delete the same type which you allocated with new: a shared_ptr<void>. This shared_ptr<void> needs to be constructed from a shared_ptr<Foo> because then it has a deleter which knows how to destruct a Foo:
void* makeFoo() {
shared_ptr<Foo> with_type = make_shared<Foo>();
shared_ptr<void> type_erased = with_type; // Just for illustration, merge with line below!
return new shared_ptr<void>(type_erased);
}
void destroy(void * ptr) {
shared_ptr<void> * original_ptr = ptr;
delete original_ptr;
}
makeFoo returns a pointer to a shared_ptr<void>. Just with the type info stripped, i.e. as void *.
destroy assumes it is passed such a pointer. Its delete calls the destructor of shared_ptr<void>. Because the shared_ptr<void> has the deleter of the original shared_ptr<Foo>, it knows how to actually destruct the object (Foo).
Side note: OP's code changed quite a few times, but still has basic syntax errors. This is not valid C++!!
delete <shared_ptr<void>*> p;
No. There is no such "generic delete".
Alternative solution: You can insert the std::shared_ptr<void> into a map, using the address of the dynamic object as key. The deallocation function can erase the shared pointer from the map.
My following question is on memory management. I have for example an int variable not allocated dynamically in a class, let's say invar1. And I'm passing the memory address of this int to another classes constructor. That class does this:
class ex1{
ex1(int* p_intvar1)
{
ptoint = p_intvar1;
}
int* ptoint;
};
Should I delete ptoint? Because it has the address of an undynamically allocated int, I thought I don't need to delete it.
And again I declare an object to a class with new operator:
objtoclass = new ex1();
And I pass this to another class:
class ex2{
ex2(ex1* p_obj)
{
obj = p_obj;
}
ex1* obj;
};
Should I delete obj when I'm already deleting objtoclass?
Thanks!
Because it has the address of an undynamically allocated int I thought I don't need to delete it.
Correct.
Should I delete obj when I'm already deleting objtoclass?
No.
Recall that you're not actually deleting pointers; you're using pointers to delete the thing they point to. As such, if you wrote both delete obj and delete objtoclass, because both pointers point to the same object, you'd be deleting that object twice.
I would caution you that this is a very easy mistake to make with your ex2 class, in which the ownership semantics of that pointed-to object are not entirely clear. You might consider using a smart pointer implementation to remove risk.
just an appendix to the other answers
You can get rid of raw pointers and forget about memory management with the help of smart pointers (shared_ptr, unique_ptr).
The smart pointer is responsible for releasing the memory when it goes out of scope.
Here is an example:
#include <iostream>
#include <memory>
class ex1{
public:
ex1(std::shared_ptr<int> p_intvar1)
{
ptoint = p_intvar1;
std::cout << __func__ << std::endl;
}
~ex1()
{
std::cout << __func__ << std::endl;
}
private:
std::shared_ptr<int> ptoint;
};
int main()
{
std::shared_ptr<int> pi(new int(42));
std::shared_ptr<ex1> objtoclass(new ex1(pi));
/*
* when the main function returns, these smart pointers will go
* go out of scope and delete the dynamically allocated memory
*/
return 0;
}
Output:
ex1
~ex1
Should I delete obj when I'm already deleting objtoclass?
Well you could but mind that deleting the same object twice is undefined behaviour and should be avoided. This can happen for example if you have two pointers for example pointing at same object, and you delete the original object using one pointer - then you should not delete that memory using another pointer also. In your situation you might as well end up with two pointers pointing to the same object.
In general, to build a class which manages memory internally (like you do seemingly), isn't trivial and you have to account for things like rule of three, etc.
Regarding that one should delete dynamically allocated memory you are right. You should not delete memory if it was not allocated dynamically.
PS. In order to avoid complications like above you can use smart pointers.
You don't currently delete this int, or show where it's allocated. If neither object is supposed to own its parameter, I'd write
struct ex1 {
ex1(int &i_) : i(i_) {}
int &i; // reference implies no ownership
};
struct ex2 {
ex2(ex1 &e_) : e(e_) {}
ex1 &e; // reference implies no ownership
};
int i = 42;
ex1 a(i);
ex2 b(a);
If either argument is supposed to be owned by the new object, pass it as a unique_ptr. If either argument is supposed to be shared, use shared_ptr. I'd generally prefer any of these (reference or smart pointer) to raw pointers, because they give more information about your intentions.
In general, to make these decisions,
Should I delete ptoint?
is the wrong question. First consider things at a slightly higher level:
what does this int represent in your program?
who, if anyone, owns it?
how long is it supposed to live, compared to these classes that use it?
and then see how the answer falls out naturally for these examples:
this int is an I/O mapped control register.
In this case it wasn't created with new (it exists outside your whole program), and therefore you certainly shouldn't delete it. It should probably also be marked volatile, but that doesn't affect lifetime.
Maybe something outside your class mapped the address and should also unmap it, which is loosely analogous to (de)allocating it, or maybe it's simply a well-known address.
this int is a global logging level.
In this case it presumably has either static lifetime, in which case no-one owns it, it was not explicitly allocated and therefore should not be explicitly de-allocated
or, it's owned by a logger object/singleton/mock/whatever, and that object is responsible for deallocating it if necessary
this int is being explicitly given to your object to own
In this case, it's good practice to make that obvious, eg.
ex1::ex1(std::unique_ptr<int> &&p) : m_p(std::move(p)) {}
Note that making your local data member a unique_ptr or similar, also takes care of the lifetime automatically with no effort on your part.
this int is being given to your object to use, but other objects may also be using it, and it isn't obvious which order they will finish in.
Use a shared_ptr<int> instead of unique_ptr to describe this relationship. Again, the smart pointer will manage the lifetime for you.
In general, if you can encode the ownership and lifetime information in the type, you don't need to remember where to manually allocate and deallocate things. This is much clearer and safer.
If you can't encode that information in the type, you can at least be clear about your intentions: the fact that you ask about deallocation without mentioning lifetime or ownership, suggests you're working at the wrong level of abstraction.
Because it has the address of an undynamically allocated int, I
thought I don't need to delete it.
That is correct. Simply do not delete it.
The second part of your question was about dynamically allocated memory. Here you have to think a little more and make some decisions.
Lets say that your class called ex1 receives a raw pointer in its constructor for a memory that was dynamically allocated outside the class.
You, as the designer of the class, have to decide if this constructor "takes the ownership" of this pointer or not. If it does, then ex1 is responsible for deleting its memory and you should do it probably on the class destructor:
class ex1 {
public:
/**
* Warning: This constructor takes the ownership of p_intvar1,
* which means you must not delete it somewhere else.
*/
ex1(int* p_intvar1)
{
ptoint = p_intvar1;
}
~ex1()
{
delete ptoint;
}
int* ptoint;
};
However, this is generally a bad design decision. You have to root for the user of this class read the commentary on the constructor and remember to not delete the memory allocated somewhere outside class ex1.
A method (or a constructor) that receives a pointer and takes its ownership is called "sink".
Someone would use this class like:
int* myInteger = new int(1);
ex1 obj(myInteger); // sink: obj takes the ownership of myInteger
// never delete myInteger outside ex1
Another approach is to say your class ex1 does not take the ownership, and whoever allocates memory for that pointer is the responsible for deleting it. Class ex1 must not delete anything on its destructor, and it should be used like this:
int* myInteger = new int(1);
ex1 obj(myInteger);
// use obj here
delete myInteger; // remeber to delete myInteger
Again, the user of your class must read some documentation in order to know that he is the responsible for deleting the stuff.
You have to choose between these two design decisions if you do not use modern C++.
In modern C++ (C++ 11 and 14) you can make things explicit in the code (i.e., do not have to rely only on code documentation).
First, in modern C++ you avoid using raw pointers. You have to choose between two kinds of "smart pointers": unique_ptr or shared_ptr. The difference between them is about ownership.
As their names say, an unique pointer is owned by only one guy, while a shared pointer can be owned by one or more (the ownership is shared).
An unique pointer (std::unique_ptr) cannot be copied, only "moved" from one place to another. If a class has an unique pointer as attribute, it is explicit that this class has the ownership of that pointer. If a method receives an unique pointer as copy, it is explicit that it is a "sink" method (takes the ownership of the pointer).
Your class ex1 could be written like this:
class ex1 {
public:
ex1(std::unique_ptr<int> p_intvar1)
{
ptoint = std::move(p_intvar1);
}
std::unique_ptr<int> ptoint;
};
The user of this class should use it like:
auto myInteger = std::make_unique<int>(1);
ex1 obj(std::move(myInteger)); // sink
// here, myInteger is nullptr (it was moved to ex1 constructor)
If you forget to do "std::move" in the code above, the compiler will generate an error telling you that unique_ptr is not copyable.
Also note that you never have to delete memory explicitly. Smart pointers handle that for you.
I am just learning about smart pointers, and I am having trouble assigning a pre-existing location of a variable to the standard library's shared pointer.
For example, lets say you have an int x, which you do not know the value of. With normal pointers, I just did
int* ptr;
ptr = &x;
I tried both that with shared pointers, and
std::tr1::shared_ptr<int> ptr;
ptr = std::make_shared<int> (&x)
So i'm fairly lost as to how to do it.
You wouldn't (usually) make a smart pointer point to an existing variable. A smart pointer manages the lifetime of a dynamically allocated object, deleting it after use; pointing it to something that wasn't dynamically allocated will cause an error if it tries to delete it.
You would usually use new or make_shared to create an object, and create or assign a smart pointer with the result of that:
std::shared_ptr<int> ptr(new int(42)); // Create a new pointer to manage an object
ptr.reset(new int(66)); // Reset to manage a different object
ptr = std::make_shared<int>(53); // Use `make_shared` rather than `new`
make_shared is usually preferable to new, since it makes better use of memory and gives stronger exception-safety.
Shared pointers are used to manage dynamically allocated memory and more precisely, they manage the ownership for this memory.
Basically, a smart pointer is a materialization of the Ressource Acquisition Is Initialization, or RAII. I strongly suggest you take a look at this principle, as it is extremely useful for managing resource ownership (basically, each time you need to acquire a resource, and release it, be it memory, a database connection, a file handler, a mutex, etc.).
What it does is basically guarantee that while someone points at the dynamically allocated memory it manages, then this memory will be available, and as soon as the last (smart) pointer to this memory goes out of scope, then delete is called.
Then, it makes no sense to use smart pointers with variable that have automatic storage duration (i.e. that are removed when they go out of scope or when the object they're member of goes itself out of scope or is deleted (if it was new'd).
You should not create a smart pointer pointing to an object that is not dynamically allocated. Otherwise the smart pointer may try to delete the allocated memory which in turn will cause an error.
as soon as the reference counter of the shared_ptr reaches zero, the object will be deleted by the last shared_ptr. with smart pointers you can specify the function which shall delete that object.
the Deleter is a simple function (defaults to the usual operator delete) that has to be bound to the smart pointer, either statically via template parameter (see unique_ptr) or dynamically via constructor parameter (see shared_ptr).
// dynamically via shared_ptr:
// shared_ptrs share the pointer to the Deleter
// because they already share a common data structure for reference counting.
auto ignore = [](int* o){
std::cout<<"i will refuse to delete this object: " << o << "\n";
std::cout<<"not my responsibility." <<std::endl;
};
std::shared_ptr<int> sptr(&x,ignore);
//statically via unique_ptr:
// actually, the unique_ptr is as data structure not more than a regular pointer.
// but a pointer with special copy-constructor and destructor,
// which will most likely be inlined.
// there is no space to store a reference to a Deleter dynamically.
struct IgnorantDeleter{
void operator()(int* o){
std::cout<<"who ate my cake? " << o << "\n";
std::cout<<"but i baked it." <<std::endl;
}
};
std::unique_ptr<int,IgnorantDeleter> uptr(&x);
So as to explain about pointers and references in this question I wrote this code.
MyClass& MyClass::MyInstance()
{
static MyClass & myLocalVariable = * new MyClass(/*parameters*/);
return myLocalVariable ;
}
one of the comments, by a really impressive high reputation SO user, simply states:
*new is always wrong. ALWAYS.
It is the first time I'm told about this: Is it a famous coding standard we all should know about ? What are the reasons behind ?
I'm normally pragmatic, however this is too much even for me!
static MyClass & myLocalVariable = * new MyClass(/*parameters*/);
Seriously? Why not simply:
static MyClass myLocalVariable{/*parameters*/};
The most obvious reason is that if you don't keep a copy of the pointer which new returned, you're not likely to ever call delete on it.
On a more human level, it will make people reading your code think less of you, and that's never a good thing either.
I believe the stated user meant that allocating a static object using new is dangerous as the memory will most probably be leaked. Even more importantly your variable is not a pointer but a reference so the chance that you never free the memory returned by new is even greater(how often do you delete the address of a reference?).
THE problem is more than just useless allocations.
If nobody calls delete on it, it won't be deleted. Sure, the memory will be released when the program ends but its destructor doesn't get called.
Compare the output of using Get() and Get2() in the code below:
#include <iostream>
struct A
{
~A(){std::cout << "Deleted\n";}
};
A& Get()
{
static A & a = *new A;
return a;
}
A& Get2()
{
static A a;
return a;
}
int main()
{
//Case 1
Get();
//Case 2
Get2();
}
The output of this program is nothing when calling Get and Deleted when calling Get2.
In other words, resources with nontrivial destructors (commit-on-close file handle for example) will not be destroyed properly at program termination in case 1, but will in case 2.
The problem is that a raw new does not specify ownership. If I new up an object and return it, who owns it? Does the creating function/object own it, or does the calling function? If you return smart pointers (std::shared_ptr and std::unique_ptr) you are specifying ownership.
Not specifying ownership is one of the easiest ways to leak memory. I have the hardest time, even with professional programmers, getting people to understand ownership and work with it. This is mostly prevented by using good types (smart pointers) that specify ownership, just by existing.
type* function(); // Unspecified ownership.
// Must be well documented and all users must read
// and follow the documentation.
std::unique_ptr<type> function(); // Calling function owns returned pointer.
// Single ownership.
std::shared_ptr<type> function(); // Calling function owns returned pointer.
// Shared ownership. Can have multiple owners.
std::weak_ptr<type> function(); // Calling function references returned pointer.
// Must lock pointer to get owned object, if not deleted.
// Shared ownership. Can have multiple owners.
These different types of pointers express ownership just by existing unlike raw pointers.
As for new always being wrong. That is an overbroad generalization. std::shared_ptr is created using the global function std::make_shared. As of C++11 there is no std::make_unique, but that will be fixed in C++14. The only way to create a std::unique_ptr is to use new and immediately assign the pointer to a std::unique_ptr.
There are also places where you would want a raw pointer and to manually use new and delete, but they tend to be very low level and most programmers will rarely encounter them.
What really has me cringing about your code is not that you are using new but that you are dereferencing the pointer and assigning it to a reference. It would be almost impossible to guarantee that the destructor will ever be called. It also tends to leak memory, though in the case of assigning to a static variable it will be deallocated at program termination so you aren't really looking at memory leaking.
MyClass& MyClass::MyInstance()
{
static MyClass & myLocalVariable = * new MyClass(/*parameters*/);
return myLocalVariable ;
}
I would prefer to create to have the static variable be by value than by reference. This prevents putting the object on the heap. Depending on MyClass is could also allow the object to be mapped to memory from the executable without having to run any code to initialize it.
MyClass& MyClass::MyInstance()
{
static MyClass myLocalVariable(/*parameters*/);
return myLocalVariable ;
}
void ClassName::LocalMethod( )
{
boost::shared_ptr<ClassName> classNamePtr( this );
//some operation with classNamePtr
return;
}
Here the object is getting released when it returns from LocalMethod() since classNamePtr is out of scope. Isn't the shared_ptr smart enough to know that the ClassName object is still in scope and not to delete it?
What does it mean to create a shared_ptr to an object? It means that the holder of the shared_ptr now assumes ownership over the object. Ownership meaning that the object will be deleted when he so desires. When the holder of the shared_ptr destroys its shared_ptr, that will cause the object to potentially be destroyed, assuming that there are no other shared_ptrs to that object.
When a shared_ptr is a member of a class, that means that the lifetime of the object pointed to by the shared_ptr is at least as long as the object that the shared_ptr is a member of. When a shared_ptr is on the stack, this means that the lifetime of the object that the shared_ptr is pointing to will be at least as long as the scope it was created in. Once the object falls off the stack, it may be deleted.
The only time you should ever take a pointer and wrap it into a shared_ptr is when you are allocating the object initially. Why? Because an object does not know whether it is in a shared_ptr or not. It can't know. This means that the person who creates the original shared_ptr now has the responsibility to pass it around to other people who need to share ownership of that memory. The only way shared ownership works is through the copy constructor of shared_ptr. For example:
shared_ptr<int> p1 = new int(12);
shared_ptr<int> p2 = p1.get();
shared_ptr<int> p3 = p1;
The copy constructor of shared_ptr creates shared ownership between p1 and p3. Note that p2 does not share ownership with p1. They both think they have ownership over the same memory, but that's not the same as sharing it. Because they both think that they have unique ownership of it.
Therefore, when the three pointers are destroyed, the following will happen. First, p3 will be destroyed. But since p3 and p1 share ownership of the integer, the integer will not be destroyed yet. Next, p2 will be destroyed. Since it thinks that it is the only holder of the integer, it will then destroy it.
At this point, p1 is pointing to deleted memory. When p1 is destroyed, it thinks that it is the only holder of the integer, so it will then destroy it. This is bad, since it was already destroyed.
Your problem is this. You are inside an instance of a class. And you need to call some functions of yours that take a shared_ptr. But all you have is this, which is a regular pointer. What do you do?
You're going to get some examples that suggest enable_shared_from_this. But consider a more relevant question: "why do those functions take a shared_ptr as an argument?"
The type of pointer a function takes is indicative of what that function does with its argument. If a function takes a shared_ptr, that means that it needs to own the pointer. It needs to take shared ownership of the memory. So, look at your code and ask whether those functions truly need to take ownership of the memory. Are they storing the shared_ptr somewhere long-term (ie: in an object), or are they just using them for the duration of the function call?
If it's the latter, then the functions should take a naked pointer, not a shared_ptr. That way, they cannot claim ownership. Your interface is then self-documenting: the pointer type explains ownership.
However, it is possible that you could be calling functions that truly do need to take shared ownership. Then you need to use enable_shared_from_this. First, your class needs to be derived from enable_shared_from_this. Then, in the function:
void ClassName::LocalMethod()
{
boost::shared_ptr<ClassName> classNamePtr(shared_from_this());
//some operation with classNamePtr
return;
}
Note that there is a cost here. enable_shared_from_this puts a boost::weak_ptr in the class. But there is no virtual overhead or somesuch; it doesn't make the class virtual. enable_shared_from_this is a template, so you have to declare it like this:
class ClassName : public boost::enable_shared_from_this<ClassName>
Isn't the shared_ptr smart enough to know that the ClassName object is
still in scope and not to delete it?
That's not how shared_ptr works. When you pass a pointer while constructing a shared_ptr, the shared_ptr will assume ownership of the pointee (in this case, *this). In other words, the shared_ptr assumes total control over the lifetime of the pointee by virtue of the fact that the shared_ptr now owns it. Because of this, the last shared_ptr owning the pointee will delete it.
If there will be no copies of classNamePtr outside of ClassName::LocalMethod(), you can pass a deleter that does nothing while constructing classNamePtr. Here's an example of a custom deleter being used to prevent a shared_ptr from deleting its pointee. Adapting the example to your situation:
struct null_deleter // Does nothing
{
void operator()(void const*) const {}
};
void ClassName::LocalMethod()
{
// Construct a shared_ptr to this, but make it so that it doesn't
// delete the pointee.
boost::shared_ptr<ClassName> classNamePtr(this, null_deleter());
// Some operation with classNamePtr
// The only shared_ptr here will go away as the stack unwinds,
// but because of the null deleter it won't delete this.
return;
}
You can also use enable_shared_from_this to obtain a shared_ptr from this. Note that the member function shared_from_this() only works if you have an existing shared_ptr already pointing to this.
class ClassName : public enable_shared_from_this<ClassName>
{
public:
void LocalMethod()
{
boost::shared_ptr<ClassName> classNamePtr = shared_from_this();
}
}
// ...
// This must have been declared somewhere...
shared_ptr<ClassName> p(new ClassName);
// before you call this:
p->LocalMethod();
This is the more appropriate, "official" method and it's much less hackish than the null deleter method.
It could also be that you don't actually need to create a shared_ptr in the first place. What goes into the section commented //some operation with classNamePtr? There might be an even better way than the first two ways.