shared_ptr与指针共存

时间:2010-12-17 14:26:39

标签: c++ pointers smart-pointers

我有如下功能

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路由并从堆中分配所有内容吗?

7 个答案:

答案 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));
}