c++智能指针的使用 | 字数总计: 2.3k | 阅读时长: 11分钟 | 阅读量:
c++智能指针的使用 官方参考
普通指针的烦恼:内存泄漏,多次释放,提前释放
智能指针 负责自动释放所指向的对象。
三种智能指针 shared_ptr,unique_ptr,weak_ptr;
将shared_ptr存放在一个容器中,不再需要它的时候,要erase掉。
allocator负责封装堆内存管理的对象,它们在整个标准库中使用,特别是STL容器使用它们来管理容器内部的所有内存分配,大部份情况下,程序员不用理会,标准容器使用默认的分配器称为std :: allocator。
shared_ptr shared_ptr
多个指针指向相同的对象;
使用引用计数,引用计数是线程安全的,但是对象的读写需要加锁。
不可以直接将指针直接赋值给一个智能指针,因为指针指针是一个类。
get获取原始指针
最大的陷阱就是循环引用,这会导致内存无法正确释放,导致内存泄漏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <iostream> #include <memory> #include <thread> #include <chrono> #include <mutex> struct Base { Base () { std::cout << " Base::Base()\n" ; } ~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; { 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 (); 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" ; }
可能的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> #include <memory> std::weak_ptr<int > gw; void observe () { std::cout << "use_count == " << gw.use_count () << ": " ; if (auto spt = gw.lock ()) { std::cout << *spt << "\n" ; } else { std::cout << "gw is expired\n" ; } } int main () { { auto sp = std::make_shared <int >(42 ); gw = sp; observe (); } observe (); }
输出:
1 2 use_count == 1: 42 use_count == 0: gw is expired
unique_ptr unique_ptr
唯一拥有对象
通过reset方法重新指定
通过release方法释放所有权
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #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" ; } }; 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>(); auto q = pass_through (std::move (p)); assert (!p); q->bar (); } std::cout << "Runtime polymorphism demo\n" ; { std::unique_ptr<B> p = std::make_unique <D>(); p->bar (); std::vector<std::unique_ptr<B>> v; v.push_back (std::make_unique <D>()); v.push_back (std::move (p)); v.emplace_back (new D); for (auto & p: v) p->bar (); } 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) std::cout << (char )std::fgetc (fp.get ()) << '\n' ; } 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->bar (); } std::cout << "Array form of unique_ptr demo\n" ; { std::unique_ptr<D[]> p{new D[3 ]}; } }
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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。最终,内存没有被释放完全。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 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; std::cout << "c.use_count() = " << c.use_count () << std::endl; cout<<endl; } std::cout << "p.use_count() = " << wpp.use_count () << std::endl; std::cout << "c.use_count() = " << wpc.use_count () << std::endl; return 0 ; }
运行结果
1 2 3 4 5 6 7 8 9 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智能指针引用对方,来避免循环引用导致的内存泄漏问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <iostream> #include <memory> class Child ;class Parent ;class Parent {private : std::weak_ptr<Child> ChildPtr; public : void setChild (std::shared_ptr<Child> child) { this ->ChildPtr = child; } void doSomething () { 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; std::cout << c.use_count () << std::endl; } std::cout << wpp.use_count () << std::endl; std::cout << wpc.use_count () << std::endl; return 0 ; }
运行结果