开发者

Passing smart-pointer around C++

开发者 https://www.devze.com 2023-01-26 11:45 出处:网络
I am currently using C++0x shared_ptr. I have a utility function which needs to take in a pointer to an object and the utility function only perform reading operation from the object and I could be su

I am currently using C++0x shared_ptr. I have a utility function which needs to take in a pointer to an object and the utility function only perform reading operation from the object and I could be sure that the object still exist when it need to dereference the pointer. I am wondering what is the best practice in passing the smart pointer down to a function like that? Should I pass the smart pointer by reference, by value, or could I get the raw pointer and pass it开发者_如何学Go to the function? Also, which methods give the best performance?

Thanks.


  • pass by value works OK for me
  • pass by reference might be recommended by someone else, but I don't think it's worth it
  • getting the raw pointer and passing it must be done only to interact with legacy code
  • The whole point of SP is that your object will die only if no references on it exist, so if you have an SP at hand it means the pointee is still alive (if shared pointers are used correctly)


If you only dereference the pointer in the body of the function and don't need to store the pointer for later use (which would implied taking on some sort of shared ownership), then making the function take a raw pointer (or just a reference if there is no need to handle null / no object supplied) is the most flexible as it allows the use from code that uses any sort of ownership semantics, e.g. a pointer to a local or static variable, a member of an array or another struct or a smart pointer of some sort. It is an almost universal convention that if you pass a pointer to something into a function that the caller keeps that object alive until the function exits. In fact, you would have to be in a multi-threaded environment to break this.

Only if you need to store the pointer for later use does the function need to co-operate with the caller about ownership. In this case you need to pass the shared_ptr which you can do by value or by const reference, it's not greatly important which as shared_ptr are cheap to copy. If you're very concerned about performance you can profile to see which one is better.


You only need to pass a smart pointer to a function if you mean to also specify ownership of the pointer.

I have recently written an article about ownership semantics when passing std::unique_ptr to/from functions. Have a look http://timoch.com/blog/2013/04/std-unique_ptr-semantic/

Given structure A :

struct A { int field; };

Passing a raw pointer

// 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;
}    

Passing by value

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
}

Passing by non-const reference

// 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
    }
}

Passing by 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
}

The same applies for std::shared_ptr. If the function simply uses the pointer and returns, there is no reason to pass a std::shared_ptr. You only need to pass a std::shared_ptr when you mean to give shared ownership of the pointed object to the component you call into. For example, if the function will store the pointer and expect to be able to use it after it returns (when called again for example).

Hope it helps.

TiMoch


I will go for the third option. If you are designing a function that uses an object for some time, with the guarantee that the object will be alive for the whole duration of the call, then the function signature should not take a pointer at all:

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

There is no reason to limit foo to use shared_ptr, as written above you can pass references into objects that are not even dynamically allocated. By not passing a pointer at all (not even a raw pointer) it is clearly stating that there is no ownership semantics associated to the function (i.e. it will not keep a pointer for later use, it will not delete the object), and it is stating that the object should be alive for the whole duration of the call.


The answer depends on the smart pointer type. Shared pointers you can pass by value, like this :

class A
{
  // implementation
};

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

but for example std::auto_ptr, you can not do the above, as the object gets destructed :

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

In the 2nd case, you should pass by reference :

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


The answer is very simple - do whatever you will do with every other class with relatively cheap copy expense.

Note copying smart pointers is not as cheap as copying integers for example. It is probably in between of integers and small std::string. (I worked in a project with all smart pointers send by value and incrementing and decrementing the reference count of smart pointers was taking between 4-6% of the time).

If you prefer to pass std::string by reference for example you should probably do it for smart pointers as well.

If you use reference (what I personally recommend) use const reference - this way you will allow the compiler to do implicit upcast of the referenced class where it is needed.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号