我有如下功能
void a(shared_ptr<X> val) {...}
有时,我想传入堆分配的对象
shared_ptr<X> v(new X());
a(v);
其他时候,我想传入一个堆栈分配的对象
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;
};
使函数接受智能指针的最佳方法是什么,还让智能指针引用堆栈分配的对象?
或者我应该去Java路由并从堆中分配所有内容吗?
答案 0 :(得分:9)
我只是让函数采用这样的模板参数:
template<class P>
void func(P ptr) {
// use ptr like a pointer as usual
// for example
*ptr = 10;
}
然后你可以这样做:
shared_ptr<int> v(new int);
int x;
func(v); // works
func(&x); // also works
您可以传递具有类似接口的指针的任何,例如,迭代器:
std::vector<int>::iterator it = v.begin();
func(it); // still works :-)
答案 1 :(得分:6)
如果你正在使用堆栈分配的内存,你不应该使用智能指针,句点。 Boost智能指针的设计围绕着在一些范围内需要删除的堆分配内存的概念,因此没有明显的概念告诉这样的指针不释放某些内存。
您可以指定删除函数作为智能指针的第二个参数,并使该函数不删除内存,但是读取代码的任何人都不清楚传递给此函数的内存的生命周期。因此,如果您需要使用智能指针,则应该在堆上分配传递给它们的任何内存。
答案 2 :(得分:5)
您可以使用null_deleter
并仍使用shared_ptr
。
这样,shared_ptr
在销毁时不会删除关联的指针。
struct null_deleter
{
void operator() (void const*) const {};
};
然后你可以这样使用它:
Foo foo;
shared_ptr<Foo> pfoo(&foo, null_deleter());
然而,这并不完全是shared_ptr
的用途。如果您在程序中经常使用此 hack ,您可能还想重新考虑您的设计,因为它可能表明出现了问题。
考虑以下情况:
shared_ptr<Foo> pfoo;
{
Foo foo;
pfoo.reset(&foo, null_deleter());
} // foo gets destroyed
// Now pfoo points to freed data !
pfoo->doSomething(); // Undefined behavior
答案 3 :(得分:3)
只需使该函数采用普通(非智能)指针即可。您无需在任何地方使用智能指针!
答案 4 :(得分:3)
你没有指定为什么你要传递一个shared_ptr,所以我将根据假设没有充分理由提供答案。
我通常希望如果一个函数采用了智能指针,那是因为该函数可能希望以某种方式保存或操纵指针。由于您的示例未通过引用传递shared_ptr,因此我可以假设您不希望能够从函数内部删除该对象。也许您希望保存共享引用以供以后使用,但与使用基于堆栈的对象的函数版本不兼容。根据您的规范,这些似乎是两个根本不同的功能(如果您真的需要采用shared_ptr并且您还想接受堆栈变量)。就像我之前说的那样,我假设你实际上不需要函数内的shared_ptr 。
所以这是你的代码的一个版本,它做了我猜你需要的......
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;
};
此方法提供与template-based solution相同的功能,而不需要函数a
在内部使用基于指针的语法。
另一个例子,源自埃文的答案......
std::vector<int>::iterator it = v.begin();
a(*it); // also works
答案 5 :(得分:2)
我认为尝试将自动堆栈变量自动堆叠到shared_ptr
中是有风险的,因为你基本上废除了shared_ptr
的语义。即使你克服了删除问题,你仍然会留下这样一个事实,即你有一个shared_ptr
指向一个在这个意义上并不真正想要“共享”的对象,以及所有权所有shared_ptr
附带的内存管理语义都会出现。如果有人没有意识到你在做什么,那将是一场噩梦。
答案 6 :(得分:1)
shared_ptr的预期用途是它的名称:共享对象。您无法共享具有自动存储持续时间的对象。一旦他们的范围结束,该范围之外的任何对象都不能依赖于它们的存在。
就像ereOn指出的那样,你可以通过传递一个空删除器来获得一些东西,但这对于该函数的用户来说是一种痛苦。我的第一个想法是为函数创建一个重载,它接受一个引用并简单地包装shared_ptr版本:
void a(X &val){
a(shared_ptr<X>(&val, null_deleter()));
}
这至少可以使用户免于处理严重的黑客攻击。它是否更安全取决于a()
的语义。如果您正在获取指针并将其存储在a()
返回后将存在的某个全局/持久对象中,则仍然存在未定义行为的风险。在这种情况下,解决问题的唯一安全方法是制作副本:
void a(const X &val){
a(shared_ptr<X>(new X(val));
}