C++0x unique_ptr replaces scoped_ptr taking ownership?

前端 未结 5 569
别跟我提以往
别跟我提以往 2020-12-08 21:24

I used to write code like this:

class P {};

class Q: public P {};

class A {
    // takes ownership
    A(P* p): p_(p) {}

    scoped_ptr

p_; }; A

相关标签:
5条回答
  • 2020-12-08 22:00

    I've upvoted comonad's answer, but with a caveat:

    Whenever you want to explicitely disallow move semantics, use a scoped_ptr const unique_ptr.

    I have not come across any use cases where a const std::unique_ptr is inferior to a boost::scoped_ptr. However I'm open to education on the subject.

    Edit:

    Here is a use case of boost::scoped_ptr that I think should fail, but does not. It does fail for std::unique_ptr:

    #include <iostream>
    
    #ifdef USE_UNIQUEPTR
    
    #include <memory>
    typedef std::unique_ptr<int> P;
    
    #else  // USE_UNIQUEPTR
    
    #include <boost/scoped_ptr.hpp>
    typedef boost::scoped_ptr<int> P;
    
    #endif  // USE_UNIQUEPTR
    
    int main()
    {
        P p1(new int(1));
        {
            // new scope
    #ifdef USE_UNIQUEPTR
            const P p2(new int(2));
    #else  // USE_UNIQUEPTR
            P p2(new int(2));
    #endif  // USE_UNIQUEPTR
            swap(p1, p2);  // should fail!
        }
        std::cout << *p1 << '\n';
    }
    

    If the promise of boost::scoped_ptr is that its resource will not escape the current scope, then it is not as good at holding that promise as a const std::unique_ptr. If we want to compare const boost::scoped_ptr to const::std::unique_ptr, I have to ask: for what purpose? They seem the same to me, except that a const std::unique_ptr allows customized construction and destruction.

    0 讨论(0)
  • 2020-12-08 22:01

    IMO it is better to use unique_ptr as it provides an additional feature: move semantics. i.e. you can write a move constructor, etc for your class, unlike scoped_ptr. Also, unique_ptr doesn't have an overhead associated with it as it is the case with scoped_ptr, so it is a superior facility. A decision of a rewrite is up to you of course, in case you don't need move semantics then there is no point of the rewrite. Don't forget that unique_ptr is from the standard library, so it must be provided with any compliant implementation of C++0x(when it becomes reality of course :)!

    0 讨论(0)
  • 2020-12-08 22:05

    I have to disagree with AraK on one being superior. There is no such thing as a superior choice between the two as it often depends on usage. That's like saying a SmartCar is superior to a pick-up truck for all uses because it's lighter and faster. In reality, sometimes you need a truck and sometimes you don't. Your choice of pointer should be based on what you need.

    The nice thing about scoped_ptr is it adds a level of safety. By using scoped_ptr you are delcaring that the memory created will exist only for that scope and no more, thus you get compile-time protection against attempting to move it or transfer it.

    So, if you want to create somethign but limit it's scope, used scoped_ptr. If you want to create something and have ownership be movable, use unique_ptr. If you want to create something and share that pointer and cleanup when all referencers are gone, use shared_ptr.

    0 讨论(0)
  • 2020-12-08 22:09
    • A auto_ptr is a pointer with copy and with move semantics and ownership (=auto-delete).
    • A unique_ptr is a auto_ptr without copy but with move semantics.
    • A scoped_ptr is a auto_ptr without copy and without move semantics.

      auto_ptr‍s are allways a bad choice – that is obvious.

      Whenever you want to explicitely have move semantics, use a unique_ptr.

      Whenever you want to explicitely disallow move semantics, use a scoped_ptr.

    • All pointers allow swap semantics, like p.swap(q). To disallow those, use any const …_ptr.

    There are situations, where you want to use a scoped_ptr pointing to one of several interchangeable objects: Because of the absence of move semantics, it is quite safe (in respect to obvious bugs) that it will not accidentally point to null because of an unintended move. Worth to mention: scoped_ptr‍s can still be swap‍ped efficiently. To make it movable and/or copyable – but still with these swap semantics – you might want to consider using a shared_ptr pointing to a scoped_ptr pointing to an exchangeable (via scoped_ptr::swap) object.

    See stackoverflow:smart-pointers-boost-explained for further details.

    0 讨论(0)
  • 2020-12-08 22:13

    Edit: my bad, you DO need to write move(p) inside the initialiser. std::move treats whatever it's given as an rvalue reference, and in your case, even though your argument is an rvalue reference to something, passing it to something else (like p_'s constructor) will pass an lvalue reference, never an rvalue reference by default.

    Per Karu's comment, also added necessary includes to made my code compilable.

    For example:

    #include <memory>
    #include <cassert>
    #include <vector>
    using namespace std;
    
    class A {};
    
    class B {
    public:
      void takeOwnershipOf(unique_ptr<A>&& rhs) {
        // We need to explicitly cast rhs to an rvalue when passing it to push_back
        // (otherwise it would be passed as an lvalue by default, no matter what
        // qualifier it has in the argument list).  When we do that, the move
        // constructor of unique_ptr will take ownership of the pointed-to value
        // inside rhs, thus making rhs point to nothing.
        owned_objects.push_back(std::move(rhs));
      }
    
    private:
      vector<unique_ptr<A>> owned_objects;
    };
    
    int main() {
      unique_ptr<B> b(new B());
      // we don't need to use std::move here, because the argument is an rvalue,
      // so it will automatically be transformed into an rvalue reference.
      b->takeOwnershipOf( unique_ptr<A>(new A()) );
    
      unique_ptr<A> a (new A());
      // a points to something
      assert(a);
      // however, here a is an lvalue (it can be assigned to). Thus we must use
      // std::move to convert a into an rvalue reference.
      b->takeOwnershipOf( std::move(a) );
      // whatever a pointed to has now been moved; a doesn't own it anymore, so
      // a points to 0.
      assert(!a);
      return 0;
    }
    

    Also, in your original example, you should rewrite class A like this:

    class A { // takes ownership A(unique_ptr

    && p): p_(std::move(p)) {}

    unique_ptr<P> p_;
    

    };

    0 讨论(0)
提交回复
热议问题