智能指针

  • 智能指针 Smart Pointer
    • auto_ptr
    • 智能指针的自实现
    • shared_ptr
      • weak_ptr
    • unique_ptr

智能指针 Smart Pointer

用来改善传统指针的问题

  1. 需要手动管理内存

  2. 容易发生内存泄露(忘记释放、出现异常等)

    比如:

    一旦test()抛出异常,p指针的内存就不会被释放,发生内存泄露。

  3. 释放后产生野指针

智能指针就是为了解决传统指针存在的问题

  • auto_prt:属于C++98标准,在C++11已不推荐使用(有缺陷,比如不能用于数组
  • shared_ptr:属于C++11标准
  • unique_ptr:属于C++11标准

auto_ptr

class Person {int m_age;
public:Person() {cout << "Person()" << endl;}Person(int age): m_age(age) {cout << "Person(int)" << endl;}~Person() {cout << "~Person()" << endl;}void run() {cout << "run()" << endl;}
};void test() {// 可以理解为:智能指针p指向了堆空间的Person对象auto_ptr<Person> p(new Person(20));p->run();
}int main() {test();getchar();return 0;
}// 输出:
// Person(int)
// run()
// ~Person()
// 并没有调用析构函数,但会自动调用

智能指针不能指向栈空间的对象!因为智能指针是堆空间指针,析构时会double free!

{Person person(20); // 创建在栈空间auto_ptr<Person> p(&person); // 堆空间指针指向栈空间对象p->run();// 会调用两次析构函数
}

不能作用于数组

{auto_ptr<Person> p(new Person[10]); // 一个数组,里面有10个Person对象p->run();
}
// 构造函数会调用10次,但析构函数只会调用一次,然后抛出异常

可以使用shared_ptr

智能指针的自实现

template <typename T>
class SmartPointer {
private:T *m_obj;
public:SmartPointer(T *obj): m_obj(obj) {}~SmartPointer() {if (m_obj == nullptr) return;delete m_obj;}T *operator->() { // 为了可以使用T类的成员函数return m_obj;}
}; int main() {{SmartPointer<Person> p(new Person(20));p->run();// 看似是一个指针,实则是一个对象,重载了运算符}getchar();return 0;
}

shared_ptr

  • 多个shared_ptr可以指向同一个对象,当最后一个shared_ptr在作用于范围内结束时,对象才会被自动释放

    {shared_ptr<Person> p1(new Person(10));shared_ptr<Person> p2 = p1;shared_ptr<Person> p3 = p2;shared_ptr<Person> p4(p3);    // 本质上都是p1// 当最后一个指针(p4)结束后,p1才会被自动释放
    }
    
    • 可以通过一个已存在的智能指针初始化一个新的智能指针

      shared_ptr<Person> p1(new Person());
      shared_ptr<Person> p2(p1);
      
    • 针对数组的用法

      shared_ptr<Person> ptr1(new Person[5]{}, [](Person *p) { delete[] p;});
      // 传入lambda表达式表明想要怎么析构
      
  • shared_ptr的原理

    • 一个shared_ptr会对一个对象产生强引用

    • 每个对象都有个与之对应的强引用计数,记录着当前对象被多少个shared_ptr强引用着

      • 可以通过shared_ptr的use_count函数获得强引用计数
      {shared_ptr<Person> p1(new Person(10));cout << p1.use_count() << endl;shared_ptr<Person> p2 = p1;cout << p1.use_count() << endl;shared_ptr<Person> p3 = p2;cout << p1.use_count() << endl;shared_ptr<Person> p4(p3);  cout << p1.use_count() << endl;
      }
      //  打印1 2 3 4
      
      • 当有一个新的shared_ptr指向对象时,对象的强引用计数就会+1
      • 当有一个shared_ptr销毁时,比如作用域结束,对象的强引用计数就会-1
      • 当一个对象的强引用计数为0时(没有任何shared_ptr指向对象时),对象就会自动销毁(析构
  • 循环引用

    你强引用我,我强引用你,大家都别销毁

    class Person;class Car {
    public:shared_ptr<Person> m_person;~Car() {cout << "~Car()" << endl;}
    };class Person {
    public:shared_ptr<Person> m_car;~Person() {cout << "~Person()" << endl;}
    };int main() {{shared_ptr<Person> person(new Person());shared_ptr<Car> car(new Car());person->m_car = car;car->m_person = person;// 不会析构,会造成内存泄漏}getchar();return 0;
    }
    

Person和Car的强引用计数都是2

一旦main函数内的大括号结束,栈空间的person 和car的强引用结束,但堆空间的person和car相互的强引用并不结束

所以Person和Car的强引用计数变为1,无法销毁

如何解决呢?

使用弱引用(weak_ptr)

weak_ptr
  • 会对一个对象产生弱引用

  • 可以指向对象解决shared_ptr的循环引用问题

    class Person;class Car {
    public:weak_ptr<Person> m_person;~Car() {cout << "~Car()" << endl;}
    };class Person {
    public:shared_ptr<Person> m_car;~Person() {cout << "~Person()" << endl;}
    };int main() {{shared_ptr<Person> person(new Person());shared_ptr<Car> car(new Car());person->m_car = car;car->m_person = person;// 不会析构,会造成内存泄漏}getchar();return 0;
    }
    

当栈空间的Person和Car被销毁后,Person先销毁,因为Person强引用计数为0

Person被销毁后,Car的强引用也为0了,所以Car也会被销毁

unique_ptr

unique_ptr也会对一个对象产生强引用,它可以确保同一时间只有1个指针指向对象

int main() {{unique_ptr<Person> p1(new Person());unique_ptr<Person> p2(p1); // 报错,不能把两个指针指向同一个对象![在这里插入图片描述](https://img-blog.csdnimg.cn/836d4fe420e64b24907529945889fda4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASGFsbHVjaW5hdGlvbg==,size_20,color_FFFFFF,t_70,g_se,x_16)}getchar();return 0;
}
  • 当unique_ptr销毁时(作用域结束时),其指向的对象也就自动销毁了
  • 可以使用std::move函数转移unique_ptr的所有权
int main() {unique_ptr<Person> p0;{unique_ptr<Person> p1(new Person());p0 = std::move(p1);}// 将p1的指向权转移给p0// 所以当大括号结束后p0指向的对象不销毁,因为p0的作用域getchar();return 0;
}

【C++】智能指针 Smart Pointer相关推荐

  1. [C++] 什么是智能指针(Smart Pointer)以及何时使用

    答案 1 智能指针是一个类,它封装了一个原始的C++指针,以管理所指对象的生命期.没有单一的智能指针类型,但所有这些都尝试以实用的方式抽象原始指针. 智能指针应优先于原始指针. 如果你觉得你需要使用指 ...

  2. C++中智能指针的设计和使用

    智能指针(smart pointer)是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露.它的一种通用实现技术是使用引用计数(reference ...

  3. C++智能指针(设计和使用)

    前段时间学习project Anarchy引擎,底层很多都是用的只能指针,学了之后一直没用过的,到现在终于算是见到实际作用了,现在重新温故下 智能指针(smart pointer)是存储指向动态分配( ...

  4. c++11 智能指针 unique_ptr、shared_ptr与weak_ptr

    C++11中有unique_ptr.shared_ptr与weak_ptr等智能指针(smart pointer),定义在<memory>中. 可以对动态资源进行管理,保证任何情况下,已构 ...

  5. 智能指针——unique_ptr

    1. 开篇 unique_ptr(唯一)是一种定义在中的智能指针(smart pointer).不能进行复制操作只能进行移动操作. unique是独特的.唯一的意思,故名思议,unique_ptr可以 ...

  6. 指针辨析:悬垂指针、哑指针、野指针、智能指针

    原文地址:https://blog.csdn.net/zhaojinjia/article/details/8770989 悬垂指针: 1:提出的原因: 请看下面的代码片段: int *p=NULL; ...

  7. Effective Modern C++ 第四章,C++智能指针

    智能指针 Smart Pointer 一些说明: C++98中有智能指针std::auto_ptr,在C++11中,出现std::unique_ptr.std::unique_ptr包含了std::a ...

  8. c语言智能指针是什么,C++ 智能指针深入解析

    1. 为什么需要智能指针?简单的说,智能指针是为了实现类似于Java中的垃圾回收机制.Java的垃圾回收机制使程序员从繁杂的内存管理任务中彻底的解脱出来,在申请使用一块内存区域之后,无需去关注应该何时 ...

  9. c++string 加引号_C++|引用计数与shared_ptr智能指针(以实现String类为例)

    C++ 中,动态内存的管理是通过一对运算符来完成的,new 用于申请内存空间,调用对象构造函数初始化对象并返回指向该对象的指针.delete接收一个动态对象的指针,调用对象的析构函数销毁对象,释放与之 ...

最新文章

  1. 运维工程师该干什么学些什么
  2. linux 系统安装配置 zabbix服务(源码安装)
  3. MySQL中的浮点数类型和定点数类型
  4. 微服务架构在云端的应用
  5. oracle package lock,Oracle 11g下重现library cache lock等待事件
  6. 什么是HLS(HTTP Live Streaming)?
  7. 栈和队列都是限制存取点的线性结构_栈的练习以及解析
  8. 利用python创建学生管理系统软件代码赏析
  9. Android Call requires API level 11 (current min is 8)的解决方案
  10. Hadoop简介 (资源)
  11. 关于scanf对换行的吸收
  12. 一文读懂C++程序的结构、执行与编译
  13. HTML CSS 兼容所有浏览器的自定义鼠标样式
  14. 2010年5月18日 小细节大隐患
  15. Python 操作pdf文件-合并操作 (三)
  16. Cholesky分解
  17. windows无法连接到打印机,打印处理器不存在
  18. 城镇化进程中的粮食生产问题
  19. 计算机专业学生组装电脑配置,我打算配置一台5000左右的组装台式电脑
  20. c语言中比较两束大小,【 C 语言吧 · 文学 · 西游记 】

热门文章

  1. win上mysql忘记root密码_MySQL数据库之windows下mysql忘记root密码的解决方法
  2. python做游戏代码_利用Python基础代码语句,实现2G时代文字小游戏,世界如此简单!...
  3. JSR303数据校验-2021新版
  4. 自考计算机及应用心得体会,自考中文专业的心得体会
  5. linux如何编译php扩展,linux环境下编译php扩展
  6. easypoi导出word表格_拒绝加班,批量将word文档中的信息高效率提取出来存储到Excel中...
  7. 【若依(ruoyi)】自定义layer
  8. 【uni-app】在新窗口中打开链接
  9. 南京大学计算机学院宋教授,宋曰钦教授
  10. oracle三种删除的区别,三种批量删除PLSQL写法效率的比对