经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
c++智能指针的使用,shared_ptr,unique_ptr,weak_ptr
来源:cnblogs  作者:xutopia  时间:2022/1/17 11:12:33  对本文有异议

c++智能指针的使用

官方参考

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

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

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

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

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

shared_ptr

shared_ptr

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

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

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

get获取原始指针

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

  1. #include <iostream>
  2. #include <memory>
  3. #include <thread>
  4. #include <chrono>
  5. #include <mutex>
  6. struct Base
  7. {
  8. Base() { std::cout << " Base::Base()\n"; }
  9. // 注意:此处非虚析构函数 OK
  10. ~Base() { std::cout << " Base::~Base()\n"; }
  11. };
  12. struct Derived: public Base
  13. {
  14. Derived() { std::cout << " Derived::Derived()\n"; }
  15. ~Derived() { std::cout << " Derived::~Derived()\n"; }
  16. };
  17. void thr(std::shared_ptr<Base> p)
  18. {
  19. std::this_thread::sleep_for(std::chrono::seconds(1));
  20. std::shared_ptr<Base> lp = p; // 线程安全,虽然自增共享的 use_count
  21. {
  22. static std::mutex io_mutex;
  23. std::lock_guard<std::mutex> lk(io_mutex);
  24. std::cout << "local pointer in a thread:\n"
  25. << " lp.get() = " << lp.get()
  26. << ", lp.use_count() = " << lp.use_count() << '\n';
  27. }
  28. }
  29. int main()
  30. {
  31. std::shared_ptr<Base> p = std::make_shared<Derived>();
  32. std::cout << "Created a shared Derived (as a pointer to Base)\n"
  33. << " p.get() = " << p.get()
  34. << ", p.use_count() = " << p.use_count() << '\n';
  35. std::thread t1(thr, p), t2(thr, p), t3(thr, p);
  36. p.reset(); // 从 main 释放所有权
  37. std::cout << "Shared ownership between 3 threads and released\n"
  38. << "ownership from main:\n"
  39. << " p.get() = " << p.get()
  40. << ", p.use_count() = " << p.use_count() << '\n';
  41. t1.join(); t2.join(); t3.join();
  42. std::cout << "All threads completed, the last one deleted Derived\n";
  43. }

可能的输出:

  1. Base::Base()
  2. Derived::Derived()
  3. Created a shared Derived (as a pointer to Base)
  4. p.get() = 0x2299b30, p.use_count() = 1
  5. Shared ownership between 3 threads and released
  6. ownership from main:
  7. p.get() = 0, p.use_count() = 0
  8. local pointer in a thread:
  9. lp.get() = 0x2299b30, lp.use_count() = 5
  10. local pointer in a thread:
  11. lp.get() = 0x2299b30, lp.use_count() = 3
  12. local pointer in a thread:
  13. lp.get() = 0x2299b30, lp.use_count() = 2
  14. Derived::~Derived()
  15. Base::~Base()
  16. 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. #include <iostream>
  2. #include <memory>
  3. std::weak_ptr<int> gw;
  4. void observe()
  5. {
  6. std::cout << "use_count == " << gw.use_count() << ": ";
  7. if (auto spt = gw.lock()) { // 使用之前必须复制到 shared_ptr
  8. std::cout << *spt << "\n";
  9. }
  10. else {
  11. std::cout << "gw is expired\n";
  12. }
  13. }
  14. int main()
  15. {
  16. {
  17. auto sp = std::make_shared<int>(42);
  18. gw = sp;
  19. observe();
  20. }
  21. observe();
  22. }

输出:

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

unique_ptr

unique_ptr

唯一拥有对象

通过reset方法重新指定

通过release方法释放所有权

  1. #include <iostream>
  2. #include <vector>
  3. #include <memory>
  4. #include <cstdio>
  5. #include <fstream>
  6. #include <cassert>
  7. #include <functional>
  8. struct B {
  9. virtual void bar() { std::cout << "B::bar\n"; }
  10. virtual ~B() = default;//父类的析构函数需要定义为虚函数,防止内存泄漏
  11. };
  12. struct D : B
  13. {
  14. D() { std::cout << "D::D\n"; }
  15. ~D() { std::cout << "D::~D\n"; }
  16. void bar() override { std::cout << "D::bar\n"; }
  17. };
  18. // 消费 unique_ptr 的函数能以值或以右值引用接收它
  19. std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
  20. {
  21. p->bar();
  22. return p;
  23. }
  24. void close_file(std::FILE* fp) { std::fclose(fp); }
  25. int main()
  26. {
  27. std::cout << "unique ownership semantics demo\n";
  28. {
  29. auto p = std::make_unique<D>(); // p 是占有 D 的 unique_ptr
  30. auto q = pass_through(std::move(p));
  31. assert(!p); // 现在 p 不占有任何内容并保有空指针
  32. q->bar(); // 而 q 占有 D 对象
  33. } // ~D 调用于此
  34. std::cout << "Runtime polymorphism demo\n";
  35. {
  36. std::unique_ptr<B> p = std::make_unique<D>(); // p 是占有 D 的 unique_ptr
  37. // 作为指向基类的指针
  38. p->bar(); // 虚派发
  39. std::vector<std::unique_ptr<B>> v; // unique_ptr 能存储于容器
  40. v.push_back(std::make_unique<D>());
  41. v.push_back(std::move(p));
  42. v.emplace_back(new D);
  43. for(auto& p: v) p->bar(); // 虚派发
  44. } // ~D called 3 times
  45. std::cout << "Custom deleter demo\n";
  46. std::ofstream("demo.txt") << 'x'; // 准备要读的文件
  47. {
  48. std::unique_ptr<std::FILE, void (*)(std::FILE*) > fp(std::fopen("demo.txt", "r"),
  49. close_file);
  50. if(fp) // fopen 可以打开失败;该情况下 fp 保有空指针
  51. std::cout << (char)std::fgetc(fp.get()) << '\n';
  52. } // fclose() 调用于此,但仅若 FILE* 不是空指针
  53. // (即 fopen 成功)
  54. std::cout << "Custom lambda-expression deleter demo\n";
  55. {
  56. std::unique_ptr<D, std::function<void(D*)>> p(new D, [](D* ptr)
  57. {
  58. std::cout << "destroying from a custom deleter...\n";
  59. delete ptr;
  60. }); // p 占有 D
  61. p->bar();
  62. } // 调用上述 lambda 并销毁 D
  63. std::cout << "Array form of unique_ptr demo\n";
  64. {
  65. std::unique_ptr<D[]> p{new D[3]};
  66. } // 调用 ~D 3 次
  67. }

输出:

  1. unique ownership semantics demo
  2. D::D
  3. D::bar
  4. D::bar
  5. D::~D
  6. Runtime polymorphism demo
  7. D::D
  8. D::bar
  9. D::D
  10. D::D
  11. D::bar
  12. D::bar
  13. D::bar
  14. D::~D
  15. D::~D
  16. D::~D
  17. Custom deleter demo
  18. x
  19. Custom lambda-expression deleter demo
  20. D::D
  21. D::bar
  22. destroying from a custom deleter...
  23. D::~D
  24. Array form of unique_ptr demo
  25. D::D
  26. D::D
  27. D::D
  28. D::~D
  29. D::~D
  30. 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. class Child;
  2. class Parent;
  3. class Parent {
  4. private:
  5. std::shared_ptr<Child> ChildPtr;
  6. public:
  7. void setChild(std::shared_ptr<Child> child) {
  8. this->ChildPtr = child;
  9. }
  10. void doSomething() {
  11. if (this->ChildPtr.use_count()) {
  12. }
  13. }
  14. ~Parent() {}
  15. };
  16. class Child {
  17. private:
  18. std::shared_ptr<Parent> ParentPtr;
  19. public:
  20. void setPartent(std::shared_ptr<Parent> parent) {
  21. this->ParentPtr = parent;
  22. }
  23. void doSomething() {
  24. if (this->ParentPtr.use_count()) {
  25. }
  26. }
  27. ~Child() {}
  28. };
  29. int main() {
  30. std::weak_ptr<Parent> wpp;
  31. std::weak_ptr<Child> wpc;
  32. {
  33. std::shared_ptr<Parent> p(new Parent);
  34. std::shared_ptr<Child> c(new Child);
  35. std::cout << "p.use_count() = " << p.use_count() << std::endl;
  36. std::cout << "c.use_count() = " << c.use_count() << std::endl;
  37. p->setChild(c);
  38. c->setPartent(p);
  39. std::cout << "p.use_count() = " << p.use_count() << std::endl;
  40. std::cout << "c.use_count() = " << c.use_count() << std::endl;
  41. wpp = p;
  42. wpc = c;
  43. std::cout << "p.use_count() = " << p.use_count() << std::endl; // 2
  44. std::cout << "c.use_count() = " << c.use_count() << std::endl; // 2
  45. cout<<endl;
  46. }
  47. std::cout << "p.use_count() = " << wpp.use_count() << std::endl; // 1
  48. std::cout << "c.use_count() = " << wpc.use_count() << std::endl; // 1
  49. return 0;
  50. }

运行结果

  1. p.use_count() = 1
  2. c.use_count() = 1
  3. p.use_count() = 2
  4. c.use_count() = 2
  5. p.use_count() = 2
  6. c.use_count() = 2
  7. p.use_count() = 1
  8. c.use_count() = 1

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

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

  1. #include <iostream>
  2. #include <memory>
  3. class Child;
  4. class Parent;
  5. class Parent {
  6. private:
  7. //std::shared_ptr<Child> ChildPtr;
  8. std::weak_ptr<Child> ChildPtr;
  9. public:
  10. void setChild(std::shared_ptr<Child> child) {
  11. this->ChildPtr = child;
  12. }
  13. void doSomething() {
  14. //new shared_ptr
  15. if (this->ChildPtr.lock()) {
  16. }
  17. }
  18. ~Parent() {
  19. }
  20. };
  21. class Child {
  22. private:
  23. std::shared_ptr<Parent> ParentPtr;
  24. public:
  25. void setPartent(std::shared_ptr<Parent> parent) {
  26. this->ParentPtr = parent;
  27. }
  28. void doSomething() {
  29. if (this->ParentPtr.use_count()) {
  30. }
  31. }
  32. ~Child() {
  33. }
  34. };
  35. int main() {
  36. std::weak_ptr<Parent> wpp;
  37. std::weak_ptr<Child> wpc;
  38. {
  39. std::shared_ptr<Parent> p(new Parent);
  40. std::shared_ptr<Child> c(new Child);
  41. p->setChild(c);
  42. c->setPartent(p);
  43. wpp = p;
  44. wpc = c;
  45. std::cout << p.use_count() << std::endl; // 2
  46. std::cout << c.use_count() << std::endl; // 1
  47. }
  48. std::cout << wpp.use_count() << std::endl; // 0
  49. std::cout << wpc.use_count() << std::endl; // 0
  50. return 0;
  51. }

运行结果

  1. 2100

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

原文链接:http://www.cnblogs.com/xutopia/p/15787770.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号