zoukankan      html  css  js  c++  java
  • c++智能指针的使用,shared_ptr,unique_ptr,weak_ptr

    c++智能指针的使用

    官方参考

    普通指针的烦恼:内存泄漏,多次释放,提前释放

    智能指针 负责自动释放所指向的对象。

    三种智能指针 shared_ptr,unique_ptr,weak_ptr;

    将shared_ptr存放在一个容器中,不再需要它的时候,要erase掉。

    allocator负责封装堆内存管理的对象,它们在整个标准库中使用,特别是STL容器使用它们来管理容器内部的所有内存分配,大部份情况下,程序员不用理会,标准容器使用默认的分配器称为std :: allocator。

    shared_ptr

    shared_ptr

    多个指针指向相同的对象;

    使用引用计数,引用计数是线程安全的,但是对象的读写需要加锁。

    不可以直接将指针直接赋值给一个智能指针,因为指针指针是一个类。

    get获取原始指针

    最大的陷阱就是循环引用,这会导致内存无法正确释放,导致内存泄漏

    #include <iostream>
    #include <memory>
    #include <thread>
    #include <chrono>
    #include <mutex>
     
    struct Base
    {
        Base() { std::cout << "  Base::Base()\n"; }
        // 注意:此处非虚析构函数 OK
        ~Base() { std::cout << "  Base::~Base()\n"; }
    };
     
    struct Derived: public Base
    {
        Derived() { std::cout << "  Derived::Derived()\n"; }
        ~Derived() { std::cout << "  Derived::~Derived()\n"; }
    };
     
    void thr(std::shared_ptr<Base> p)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        std::shared_ptr<Base> lp = p; // 线程安全,虽然自增共享的 use_count
        {
            static std::mutex io_mutex;
            std::lock_guard<std::mutex> lk(io_mutex);
            std::cout << "local pointer in a thread:\n"
                      << "  lp.get() = " << lp.get()
                      << ", lp.use_count() = " << lp.use_count() << '\n';
        }
    }
     
    int main()
    {
        std::shared_ptr<Base> p = std::make_shared<Derived>();
     
        std::cout << "Created a shared Derived (as a pointer to Base)\n"
                  << "  p.get() = " << p.get()
                  << ", p.use_count() = " << p.use_count() << '\n';
        std::thread t1(thr, p), t2(thr, p), t3(thr, p);
        p.reset(); // 从 main 释放所有权
        std::cout << "Shared ownership between 3 threads and released\n"
                  << "ownership from main:\n"
                  << "  p.get() = " << p.get()
                  << ", p.use_count() = " << p.use_count() << '\n';
        t1.join(); t2.join(); t3.join();
        std::cout << "All threads completed, the last one deleted Derived\n";
    }
    

    可能的输出:

    Base::Base()
      Derived::Derived()
    Created a shared Derived (as a pointer to Base)
      p.get() = 0x2299b30, p.use_count() = 1
    Shared ownership between 3 threads and released
    ownership from main:
      p.get() = 0, p.use_count() = 0
    local pointer in a thread:
      lp.get() = 0x2299b30, lp.use_count() = 5
    local pointer in a thread:
      lp.get() = 0x2299b30, lp.use_count() = 3
    local pointer in a thread:
      lp.get() = 0x2299b30, lp.use_count() = 2
      Derived::~Derived()
      Base::~Base()
    All threads completed, the last one deleted Derived
    

    weak_ptr

    是为了配合shared_ptr而引入的一种智能指针,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。

    weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。

    成员函数expired()的功能等价于use_count()==0,

    weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象

    #include <iostream>
    #include <memory>
     
    std::weak_ptr<int> gw;
     
    void observe()
    {
        std::cout << "use_count == " << gw.use_count() << ": ";
        if (auto spt = gw.lock()) { // 使用之前必须复制到 shared_ptr
    	std::cout << *spt << "\n";
        }
        else {
            std::cout << "gw is expired\n";
        }
    }
     
    int main()
    {
        {
            auto sp = std::make_shared<int>(42);
    	gw = sp;
     
    	observe();
        }
     
        observe();
    }
    

    输出:

    use_count == 1: 42
    use_count == 0: gw is expired
    

    unique_ptr

    unique_ptr

    唯一拥有对象

    通过reset方法重新指定

    通过release方法释放所有权

    #include <iostream>
    #include <vector>
    #include <memory>
    #include <cstdio>
    #include <fstream>
    #include <cassert>
    #include <functional>
     
    struct B {
      virtual void bar() { std::cout << "B::bar\n"; }
      virtual ~B() = default;//父类的析构函数需要定义为虚函数,防止内存泄漏
    };
    struct D : B
    {
        D() { std::cout << "D::D\n";  }
        ~D() { std::cout << "D::~D\n";  }
        void bar() override { std::cout << "D::bar\n";  }
    };
     
    // 消费 unique_ptr 的函数能以值或以右值引用接收它
    std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
    {
        p->bar();
        return p;
    }
     
    void close_file(std::FILE* fp) { std::fclose(fp); }
     
    int main()
    {
      std::cout << "unique ownership semantics demo\n";
      {
          auto p = std::make_unique<D>(); // p 是占有 D 的 unique_ptr
          auto q = pass_through(std::move(p)); 
          assert(!p); // 现在 p 不占有任何内容并保有空指针
          q->bar();   // 而 q 占有 D 对象
      } // ~D 调用于此
     
      std::cout << "Runtime polymorphism demo\n";
      {
        std::unique_ptr<B> p = std::make_unique<D>(); // p 是占有 D 的 unique_ptr
                                                      // 作为指向基类的指针
        p->bar(); // 虚派发
     
        std::vector<std::unique_ptr<B>> v;  // unique_ptr 能存储于容器
        v.push_back(std::make_unique<D>());
        v.push_back(std::move(p));
        v.emplace_back(new D);
        for(auto& p: v) p->bar(); // 虚派发
      } // ~D called 3 times
     
      std::cout << "Custom deleter demo\n";
      std::ofstream("demo.txt") << 'x'; // 准备要读的文件
      {
          std::unique_ptr<std::FILE, void (*)(std::FILE*) > fp(std::fopen("demo.txt", "r"),
                                                               close_file);
          if(fp) // fopen 可以打开失败;该情况下 fp 保有空指针
            std::cout << (char)std::fgetc(fp.get()) << '\n';
      } // fclose() 调用于此,但仅若 FILE* 不是空指针
        // (即 fopen 成功)
     
      std::cout << "Custom lambda-expression deleter demo\n";
      {
        std::unique_ptr<D, std::function<void(D*)>> p(new D, [](D* ptr)
            {
                std::cout << "destroying from a custom deleter...\n";
                delete ptr;
            });  // p 占有 D
        p->bar();
      } // 调用上述 lambda 并销毁 D
     
      std::cout << "Array form of unique_ptr demo\n";
      {
          std::unique_ptr<D[]> p{new D[3]};
      } // 调用 ~D 3 次
    }
    

    输出:

    unique ownership semantics demo
    D::D
    D::bar
    D::bar
    D::~D
    Runtime polymorphism demo
    D::D
    D::bar
    D::D
    D::D
    D::bar
    D::bar
    D::bar
    D::~D
    D::~D
    D::~D
    Custom deleter demo
    x
    Custom lambda-expression deleter demo
    D::D
    D::bar
    destroying from a custom deleter...
    D::~D
    Array form of unique_ptr demo
    D::D
    D::D
    D::D
    D::~D
    D::~D
    D::~D
    

    shared_ptr循环引用的内存泄漏问题

    如下对象建模——家长与子女:a Parent has a Child, a Child knowshis/her Parent。

    从程序的运行中可以看到最终资源没有得到释放。

    一个智能指针在创建一个对象的时候初始化引用计数为 1,并把自己的指针指向创建的对象。但这个引用计数在何处?在智能指针内部?非也,这个计数是一个单独的对象来实现的,如图1,当另外一个智能指针指向这个对象的时候,便找到与这个对象对应的计数对象,并加一个引用,即 use_count++。这样多个智能指针对象便可以使用相同的引用计数。

    下面程序中,当指针p释放时,由于指针c->ParentPtr还在引用着new Child,所以这时(new Child)的use_count从2减为1。同理当指针c释放时,由于p->ChildPtr还在引用着new Parent,所以这时(new Parent)的use_count从2减为1。最终,内存没有被释放完全。

    class Child;
    class Parent;
    
    class Parent {
    private:
        std::shared_ptr<Child> ChildPtr;
    public:
        void setChild(std::shared_ptr<Child> child) {
            this->ChildPtr = child;
        }
    
        void doSomething() {
            if (this->ChildPtr.use_count()) {
            }
        }
    
        ~Parent() {}
    };
    
    class Child {
    private:
        std::shared_ptr<Parent> ParentPtr;
    public:
        void setPartent(std::shared_ptr<Parent> parent) {
            this->ParentPtr = parent;
        }
        void doSomething() {
            if (this->ParentPtr.use_count()) {
            }
        }
        ~Child() {}
    };
    
    int main() {
        std::weak_ptr<Parent> wpp;
        std::weak_ptr<Child> wpc;
        {
            std::shared_ptr<Parent> p(new Parent);
            std::shared_ptr<Child> c(new Child);
            std::cout << "p.use_count() = " << p.use_count() << std::endl; 
            std::cout << "c.use_count() = " << c.use_count() << std::endl;
            p->setChild(c);
            c->setPartent(p);
            std::cout << "p.use_count() = " << p.use_count() << std::endl; 
            std::cout << "c.use_count() = " << c.use_count() << std::endl;
            wpp = p;
            wpc = c;
    
            std::cout << "p.use_count() = " << p.use_count() << std::endl; // 2
            std::cout << "c.use_count() = " << c.use_count() << std::endl; // 2
            cout<<endl;
    
        }
            std::cout << "p.use_count() = " << wpp.use_count() << std::endl;  // 1
            std::cout << "c.use_count() = " << wpc.use_count() << std::endl;  // 1
        return 0;
    }
    

    运行结果

    p.use_count() = 1
    c.use_count() = 1
    p.use_count() = 2
    c.use_count() = 2
    p.use_count() = 2
    c.use_count() = 2
    
    p.use_count() = 1
    c.use_count() = 1
    

    shared_ptr循环引用的内存泄漏问题解决

    如下,在两个需要互相引用的类的内部,使用weak_ptr智能指针引用对方,来避免循环引用导致的内存泄漏问题。

    #include <iostream>
    #include <memory>
    
    class Child;
    class Parent;
    
    class Parent {
    private:
        //std::shared_ptr<Child> ChildPtr;
        std::weak_ptr<Child> ChildPtr;
    public:
        void setChild(std::shared_ptr<Child> child) {
            this->ChildPtr = child;
        }
    
        void doSomething() {
            //new shared_ptr
            if (this->ChildPtr.lock()) {
    
            }
        }
    
        ~Parent() {
        }
    };
    
    class Child {
    private:
        std::shared_ptr<Parent> ParentPtr;
    public:
        void setPartent(std::shared_ptr<Parent> parent) {
            this->ParentPtr = parent;
        }
        void doSomething() {
            if (this->ParentPtr.use_count()) {
    
            }
        }
        ~Child() {
        }
    };
    
    int main() {
        std::weak_ptr<Parent> wpp;
        std::weak_ptr<Child> wpc;
        {
            std::shared_ptr<Parent> p(new Parent);
            std::shared_ptr<Child> c(new Child);
            p->setChild(c);
            c->setPartent(p);
            wpp = p;
            wpc = c;
            std::cout << p.use_count() << std::endl; // 2
            std::cout << c.use_count() << std::endl; // 1
        }
        std::cout << wpp.use_count() << std::endl;  // 0
        std::cout << wpc.use_count() << std::endl;  // 0
        return 0;
    }
    

    运行结果

    2100
    

    更多编程资料详见公众号 xutopia77

  • 相关阅读:
    [LeetCode] Permutations II
    [LeetCode] Remove Duplicates from Sorted Array II
    [LeetCode] Permutations
    [LeetCode] Path Sum II
    [LeetCode] Plus One
    [LeetCode] Path Sum
    [LeetCode] Permutation Sequence
    [LeetCode] Pow(x, n)
    [LeetCode] Remove Duplicates from Sorted Array
    [LeetCode] Remove Element
  • 原文地址:https://www.cnblogs.com/xutopia/p/15787770.html
Copyright © 2011-2022 走看看