shared_ptr coexisting with pointers
I have a function as follows
void a(shared_ptr<X> val) {...}
Sometimes, I want to pass in a heap-allocated object
shared_ptr<X> v(new X());
a(v);
Other times, I want to pass in a stack-allocated object
class C
{
//doesn't work properly b/c when the shared_ptr dies it will try to delete x...
C() { a(shared_ptr<X>(&x)); }
X x;
};
What's the best way to make a function accept smart pointers, but also let the smart pointer开发者_运维问答s refer to stack allocated objects?
Or should I go the Java route and alloc everything from the heap?
I would just make the function take a template parameter like this:
template<class P>
void func(P ptr) {
// use ptr like a pointer as usual
// for example
*ptr = 10;
}
then you can do like this:
shared_ptr<int> v(new int);
int x;
func(v); // works
func(&x); // also works
You can pass anything that has a pointer like interface, for example, iterators:
std::vector<int>::iterator it = v.begin();
func(it); // still works :-)
If you're using stack allocated memory, you shouldn't be using smart pointers, period. Boost smart pointers are designed around the notion that there is heap allocated memory that you need deleted within some scope, thus there exists no obvious notion of telling such a pointer to not deallocate certain memory.
You can specify a deletion function as the second parameter to a smart pointer and make that function not delete the memory, but then anyone reading your code is going to be unclear as to the lifetime of the memory being passed into this function. So if you need to use smart pointers, you should be allocating whatever memory you pass into them on the heap.
You can use a null_deleter
and still use shared_ptr
.
This way the shared_ptr
won't delete the associated pointer when it gets destroyed.
struct null_deleter
{
void operator() (void const*) const {};
};
You can then use it that way:
Foo foo;
shared_ptr<Foo> pfoo(&foo, null_deleter());
However, that's not exactly what shared_ptr
are for. If you are using this hack constantly in your program, you might as well want to reconsider your design as it could indicate that something is wrong.
Consider the following case:
shared_ptr<Foo> pfoo;
{
Foo foo;
pfoo.reset(&foo, null_deleter());
} // foo gets destroyed
// Now pfoo points to freed data !
pfoo->doSomething(); // Undefined behavior
Simply make the function take an ordinary (non-smart) pointer. You don't have to use smart pointers everywhere!
You didn't specify why you wanted to pass a shared_ptr, so I'm going to provide an answer based on the assumption that it was for no good reason.
I would generally expect that if a function took a smart pointer it was because that function might want to save or manipulate the pointer in some way. Since your example doesn't pass the shared_ptr by reference, I can assume that you don't want to be able to delete the object from inside the function. Maybe you want to save off a shared reference for later use, but that is incompatible with the version of your function that takes a stack-based object. Based on your specifications, these would seem to be two fundamentally different functions (IF you really needed to take a shared_ptr AND you also wanted to accept a stack variable). So like I said before, I'm assuming that you actually do not need the shared_ptr inside the function.
So here's a version of your code that does what I am guessing that you need...
void a( C & val ) // just pass by reference (or const reference)
{
val.member = 10; // for example
}
shared_ptr<X> v(new X());
a(*v); // simply dereference the pointer to call the function
// verify that v holds an object if it isn't obvious from the context
if ( v ) a(*v);
class C
{
C() { a(x); } // nothing fancy required to call the function
X x;
};
This approach provides the same functionality as the template-based solution, without requiring your function a
to internally use pointer-based syntax.
One more example, derived from Evan's answer...
std::vector<int>::iterator it = v.begin();
a(*it); // also works
I think it's risky to try and shoehorn automatic stack variables into a shared_ptr
, since you're essentially doing away with the semantics of a shared_ptr
. Even if you get over the deletion problems, you'll still be left with the fact that you have a shared_ptr
which points to an object which doesn't really want to be "shared" in that sense, and the ownership and memory management semantics that come with a shared_ptr
all go out the window. It'll be a nightmare in the future if someone doesn't realize what you're doing.
The intended use of shared_ptr is in its name: Sharing objects. You can't share objects with automatic storage duration. Once their scope ends, no object outside of that scope can depend on them existing.
Like ereOn points out, you can get something going by passing in a null deleter, but that's a pain in the ass for users of the function. My first thought is to create an overload for the function which takes a reference and simply wraps the shared_ptr version:
void a(X &val){
a(shared_ptr<X>(&val, null_deleter()));
}
This at least saves the user from dealing with the gross hack. Whether or not it's any safer depends on the semantics of a()
. If you're taking the pointer and storing it in some global/persistent object that will exist after a()
returns, you still risk undefined behavior. In that case, the only safe way around the issue is to make a copy:
void a(const X &val){
a(shared_ptr<X>(new X(val));
}
精彩评论