在C ++周围传递智能指针

时间:2010-11-24 08:12:18

标签: c++

我目前正在使用C ++ 0x shared_ptr。我有一个实用程序函数,需要接受一个指向对象的指针,实用程序函数只执行从对象的读取操作,我可以确定该对象在需要取消引用指针时仍然存在。我想知道将智能指针传递给类似函数的最佳做法是什么?我应该通过引用,按值传递智能指针,还是可以获取原始指针并将其传递给函数?另外,哪种方法可以提供最佳性能?

感谢。

6 个答案:

答案 0 :(得分:3)

  • 传递值对我来说没问题
  • 通过引用传递可能是其他人推荐的,但我不认为这是值得的
  • 获取原始指针并传递它必须完成 以与旧代码进行交互
  • SP的重点是你的对象只有在没有对它的引用时才会死掉,所以如果你手头有一个SP,那就意味着指针仍然存活(如果正确使用了共享指针)

答案 1 :(得分:3)

如果你只取消引用函数体中的指针而不需要存储指针供以后使用(这意味着承担某种共享所有权),那么使函数采用原始指针(或只是一个引用,如果不需要处理null /没有提供的对象)是最灵活的,因为它允许使用任何类型的所有权语义的代码,例如指向本地或静态变量的指针,数组或其他结构的成员或某种类型的智能指针。这是一个几乎普遍的约定,如果你将一个指针传递给一个函数,调用者将该对象保持活动状态直到该函数退出。实际上,您必须处于多线程环境中才能解决这个问题。

只有当您需要存储指针供以后使用时,该功能才需要与调用者合作完成所有权。在这种情况下,您需要传递shared_ptr,您可以通过值或const引用来执行,因为shared_ptr复制起来很便宜并不重要。如果您非常关注性能,可以查看哪个更好。

答案 2 :(得分:3)

如果您还要指定指针的所有权,则只需要将智能指针传递给函数。

我最近在向函数传递std :: unique_ptr时写了一篇关于所有权语义的文章。 看看http://timoch.com/blog/2013/04/std-unique_ptr-semantic/

给出结构A:

struct A { int field; };

传递原始指针

// you make no statement of ownership
// the caller stays the owner of the pointed object
void useA(A* ptr) {
    // useA simply uses the pointed object
    // it should not delete it (because it does not own it)
    ptr->field = 2;
}    

按值传递

std::vector<std::unique_ptr<A>> container;

// the caller explicitly gives you ownership of the object
void ownA(std::unique_ptr<A> ptr) {
    // you can use the pointed object
    std::cout << ptr->field << std::endl;

    // the object pointed to by ptr is yours
    // you can place it in a container that you own
    container.push_back(std::move(ptr));
    // now container owns the pointed A instance

    // if you do not move it using std::move()
    // the pointed object is deleted automatically
}

通过非const引用

// the caller is giving you the opportunity to take ownership of the object
// it is free to not take it though
void maybeOwnA(std::unique_ptr<A> & ptr) {
    // ptr is passed by reference
    // so maybeOwnA can decide based on its own internal 
    // to take ownership of the pointed object

    // taking ownership requires that std::move is called on ptr

    // it may also simply use the pointed object and not
    // take ownership
}

void callMaybeOwnA() {
    std::unique_ptr<A> ptr = new A();
    maybeOwnA(ptr);
    // we know if we still own the instance of A
    // by looking at ptr
    if (ptr.get() != nullptr) {
        // we still own the pointed object
    }
}

传递const-reference

// this is functionally the same as passing a raw pointer
// but you force the caller to use a std::unique_ptr to call you
// the caller will not be able to use a std::shared_ptr for example
// I strongly recommend against this form. Passing a raw pointer is 
// perfectly fine
void useA(const std::unique_ptr<A> & ptr) {
    // ptr is const so you cannot call std::move on it
    // you effectively cannot own it
}

// a factory function should return a std::unique_ptr by value
// the caller owns the pointed object
std::unique_ptr<A> factory() {
    return std::unique_ptr<A>(new A());
}

void callFactory() {
    std::unique_ptr<A> ptr = factory();
    // the pointed object is deleted when ptr goes out of scope
}

同样适用于std :: shared_ptr。如果函数只是使用指针并返回,则没有理由传递std :: shared_ptr。当你的意思是将指向对象的共享所有权提供给你调用的组件时,你只需要传递一个std :: shared_ptr。例如,如果函数将存储指针并期望在它返回后能够使用它(例如,再次调用时)。

希望它有所帮助。

TiMoch

答案 3 :(得分:1)

我会选择第三种选择。如果您正在设计一个使用对象一段时间的函数,并保证对象在整个调用期间都处于活动状态,那么函数签名根本不应该使用指针:

void foo( type & t );
int main() {
   std::shared_ptr<type> sp( new type );
   foo( *sp );
}

没有理由限制foo使用shared_ptr,如上所述,您可以将引用传递给甚至不动态分配的对象。通过不传递指针(甚至不是原始指针),它清楚地表明没有与该函数关联的所有权语义(即它不会保留指针供以后使用,它不会删除该对象),并且它表示该对象应在整个通话期间保持活动状态。

答案 4 :(得分:0)

答案取决于智能指针类型。您可以通过值传递的共享指针,如下所示:

class A
{
  // implementation
};

void foo( std::shared_ptr< A > p )
{
  // use p
}

但是例如std :: auto_ptr,你无法做到这一点,因为对象被破坏了:

void boo ( std::auto_ptr< A > p )
{
 // use p
} // ops the resource pointer by p gets destructed

在第二种情况下,您应该通过引用传递:

void boo ( A  &p )
{
 // use p
}

答案 5 :(得分:0)

答案非常简单 - 用相对便宜的复制费用做任何其他课程。

注意复制智能指针并不像复制整数那样便宜。它可能介于整数和小std :: string之间。 (我在一个项目中工作,所有智能指针都按值发送,并且递增和递减智能指针的引用计数占用了4-6%的时间)。

如果您希望通过引用传递std :: string,例如,您也应该为智能指针执行此操作。

如果你使用引用(我个人推荐)使用const引用 - 这样你就可以让编译器在需要它的地方进行引用类的隐式upcast。