c + + smart pointer

Smart Pointer

Deal with c++11's smart pointer facility.

brief

Smart pointers are class objects that behave like built-in pointers but also manage objects that you create with new so that you don't have to worry about when and whether to delete them - the smart pointers automatically delete the
managed object for you at the appropriate time.

  • shared_ptr
  • weak_ptr
  • unique_ptr

shared_ptr (shared pointer)

  • referenced-counted smart pointer
  • Shared Ownership with shared_ptr

Reference Counting Smart Pointer: You can share ownership with other boost::shared_ptr type smart pointers. In this case, the object is released when the last smart pointer of the reference object is destroyed or reassigned or reset() is used. Multiple shared_ptr objects can have the same object.

Examples in inheritance:

    shared_ptr<Thing> base_ptr(new Thing(2));
    shared_ptr<Food> derived_ptr;
    ///if static_cast<Derived* >(base_ptr.get()) is valid, then the following is valid:
    base_ptr->showID();

    ///cast failed
    derived_ptr = static_pointer_cast<Food>(base_ptr);

    shared_ptr<Food> a(new Food);
//    a->showID();

    derived_ptr->showID();

Use make_shared to be more efficient

There are actually two dynamic memory allocations that happen: one for the object itself from the new, and then a second for the manager object created by the shared_ptr constructor. Since memory allocations are slow, this means that creating a shared_ptr is slow relative to using either a raw pointer, or a so-called "intrusive" reference- counted smart pointer where the reference count is a member variable of the object. To address this problem, C++11 includes a function template make_shared that does a single memory allocation big enough to hold both the manager object and the new object, passing along any constructor parameters that you specify, and returns a shared_ptr of the specified type, which can then be used to initialize the shared_ptr that you are creating (with efficient move semantics).

    shared_ptr<Thing> p(new Thing); // ouch - two allocations
    shared_ptr<Thing> p1(make_shared<Thing>()); // only one allocation!

Be careful

  • Using share_ptr copy assignment or constructor adds 1 to the reference count of shared_ptr
  • Using the reset() member function, you can make the current share_ptr empty and delete the pointer to the object
  • By assigning nullptr to share_ptr, the effect of Article 2 can be achieved.
  • Direct assignment conversion between native pointer and smart pointer is not allowed
  • Do not construct two smart pointer s with the same function directly from the native pointer, otherwise it will cause a double-deletion error
  • Native pointers can be obtained through the get() function
  • In the case of inheritance or other transformations, it can be used
    • static_pointer_cast
    • dynamic_pointer_cast
    • const_pointer_cast

weak_ptr

Weak pointers just "observe" the managed object; they don't "keep it alive" or affect its lifetime. Unlike shared_ptrs, when the last weak_ptr goes out of scope or disappears, the pointed-to object can still exist because
the weak_ptrs do not affect the lifetime of the object - they have no ownership rights. But the weak_ptr can be used to determine whether the object exists, and to provide a shared_ptr that can be used to refer to it.

Observing only managed objects has no impact on their life cycle.

1.weak_ptr build-in-pointer might zero.

void do_it(weak_ptr<Thing> wp){
shared_ptr<Thing> sp = wp.lock();
// get shared_ptr from weak_ptr 
        if(sp)
          sp->defrangulate(); // tell the Thing to do something
        else
          cout << "The Thing is gone!" << endl;
}

2.This approach is useful as a way to simply ask whether the pointed-to object still exists.

 bool is_it_there(weak_ptr<Thing> wp) {
       if(wp.expired()) {
          cout << "The Thing is gone!" << endl;
          return false;
          }
       return true;
   }

3.if the weak_ptr is expired, an exception is thrown, of type
std::bad_weak_ptr.

void do_it(weak_ptr<Thing> wp){
shared_ptr<Thing> sp(wp); // construct shared_ptr from weak_ptr
 // exception thrown if wp is expired, so if here, sp is good to go sp->defrangulate();
  // tell the Thing to do something
}
   try {
          do_it(wpx);
       }
       catch(bad_weak_ptr&)
       {
          cout << "A Thing (or something else) has disappeared!" << endl;
       }

4.inherit from enabled_shared_from_this\

Otherwise, the error will occur: pointer being freed was not allocated

class Thing:public enable_shared_from_this<Thing>{
public:
    int id;
public:
    virtual void showID() const;
    Thing();
    Thing(int _id);
    void foo();

};
void Thing::foo() {
    shared_ptr<Thing> t1 = shared_from_this();
    t1->showID();

}

If the public inherits enable_shared_from_this, the Thing class has a weak_ptr as a member variable. When the first shared_ptr is created, the weak_ptr is initialized from the first shared_ptr, and when a share_ptr pointing to this is needed, the shared_from_this() member function is called, returning a shared_ptr constructed from weak_prt so that the shared_ptr returned is the same object management as the first shared_ptr.

Be careful

  • Weak_ptr is used in conjunction with share_ptr only by copying and assigning from share_ptr, or by sources other than weak_ptr.
  • The lock() function checks whether the object pointed to by weak_ptr exists or not. If there is no share_ptr returning an empty share_ptr, it returns a share_ptr pointing to the object.
  • You can't assign nullptr to weak_ptr, you can only use reset() method
  • The expired() function returns whether weak_ptr is a non-empty object.
  • shared_from_this cannot be used in constructors
  • Use share_ptr and weak_ptr as much as possible to automate memory management.

unique_ptr

With a unique_ptr, you can point to an allocated object, and when the unique_ptr goes out of scope, the pointed-to object gets deleted, and
this happens regardless of how we leave the function, either by a return or an exception being thrown somewhere.

unique_ptr implements a unique ownership concept - an object can be owned by only one unique_ptr at a time - the opposite of shared ownership.

The unique_ptr implicitly deletes the copy constructor and the copy assignment operator, and does not allow an object to be owned by multiple unique_ptrs at the same time, as opposed to shared_ptr.

The unique ownership is enforced by disallowing (with =delete) copy construction and copy assignment.So unlike built-in pointers or shared_ptr, you can't copy or assign a unique_ptr to another unique_ptr.

move semantics: the move constructor and move assignment operator are defined for unique_ptr so that they transfer ownership from the original owner to the new owner.

The unique_ptr ownership can be transferred from the original to the new through the move constructor and the move assignment operator. The original unique_ptr after the transfer does not contain any objects.

Implicit Right-Value Conversion

 unique_ptr<Thing> create_Thing()
   {
       unique_ptr<Thing> local_ptr(new Thing);
       return local_ptr;  // local_ptr will surrender ownership
   }
void foo() {
       unique_ptr<Thing> p1(create_Thing()); // move ctor from returned rvalue
        // p1 now owns the Thing
        unique_ptr<Thing> p2; // default ctor'd; owns nothing
        p2 = create_Thing(); // move assignment from returned       rvalue // p2 now owns the second Thing
}

Explicit transformation using move assignment and move construction

unique_ptr<Thing> p1(new Thing); // p1 owns the Thing
unique_ptr<Thing> p2; // p2 owns nothing
// invoke move assignment explicitly
p2 = std::move(p1); // now p2 owns it, p1 owns nothing
// invoke move construction explicitly
unique_ptr<Thing> p3(std::move(p2)); // now p3 owns it, p2 and p1 own nothing 

Be careful

  • By reset() function or assigning nullptr to unique_ptr, you can delete objects manually.

reference

c++11 smart pointer

Posted by lisa71283 on Sun, 31 Mar 2019 08:27:30 -0700