往期地址:

  • c++系列一 —— c++的封装
  • c++系列二 —— c++的继承
  • c++系列三 —— 继承和多态特性
  • c++系列四 —— 运算符重载
  • c++系列五 —— 静态成员和静态类
  • c++系列六 —— 友元函数和友元类
  • c++系列七 —— STL编程之模板template
  • c++系列八 —— STL编程之容器类

本期主题:
智能指针


智能指针

  • 1.智能指针概述
    • 1.解决了什么问题,智能在哪里
    • 2.对智能指针的原理猜想
  • 2.智能指针使用
    • 2.1 auto_ptr
    • 2.2 unique_ptr
      • 1. 构造函数使用
      • 2. 管理器 release/reset/swap
      • 3. 观察器 get/get_deleter/operator bool
    • 2.3 auto_ptr和unique_ptr对比
      • 1.赋值运算
      • 2.作为函数参数的差异

1.智能指针概述

1.解决了什么问题,智能在哪里

智能指针解决的问题:

解决了内存泄漏的问题,对于申请在堆上的内存(c语言是malloc,c++是new),程序员不需要主动释放,智能指针自动释放

2.对智能指针的原理猜想

  1. 我们都知道栈上的内存能够自动释放,所以猜测智能指针的底层应该是类似于栈的方式
  2. 栈上的变量,创建时自动调用构造函数,退出时自动调用析构函数

按照这样的方式我们来写一份测试代码:

#include <iostream>
using namespace std;
//构造函数申请内存,析构函数释放
class smart_pointer
{public:smart_pointer(){cout << "smart_pointer()" << endl;p = NULL;}smart_pointer(int size){cout << "smart_pointer(int size)" << endl;p = new int[size];}~smart_pointer(){cout << "~smart_pointer()" << endl;if (NULL != p)delete[] p;}void test_func(){if (p != NULL) {p[0] = 1;cout << "p[0] val is " << p[0] << endl;}}private:int *p;};int main(void)
{//这里选择栈上的局部变量,这样就能够自动调用构造和析构函数smart_pointer sp_instance(3);sp_instance.test_func();return 0;
}

运行结果:

2.智能指针使用

详细内容可以参考cppreference的动态内存管理章节中,cpprefernce动态内存管理

2.1 auto_ptr

auto_ptr定义:

template< class T > class auto_ptr;

使用示例:

#include <iostream>
#include <memory>
class people
{public:people() {cout << "people()" << endl;}people(string name) {this->name = name;cout << "people(string name)" << endl;}~people() {cout << "~people()" << endl;}void print_name(void){cout << "name: " << this->name << endl;}
private:string name;};int main(void)
{//p1是people对象的智能指针auto_ptr<people> p1(new people("jason"));p1->print_name();
}

运行结果:
//这里需要使用 --std=c++98的原因是 autoptr在C++17中移除了,所以需要告诉编译器用c++98的方式去编译,不然会编译报错

2.2 unique_ptr

unique_ptr的定义如下:

template<class T,class Deleter = std::default_delete<T>
> class unique_ptr;

1. 构造函数使用

unique_ptr有多种构造函数,详细的描述参考cpprefernce,这里我们讲几种:

struct Foo {Foo() {cout << "Foo default ctor\n";}~Foo() {cout << "~Foo dtor\n";}
};//删除器
struct D {D() {cout << "D default ctor\n";}D(const D&) {cout << "D copy ctor\n";}void operator()(Foo* p) const {cout << "D is deleting Foo\n";delete p;};
};int main(void)
{cout << "Example constructor(1)...\n";unique_ptr<Foo> up1;unique_ptr<Foo> up1b(nullptr);cout << "Example constructor(2)...\n";//添加代码括号,改变up2变量的生命周期,退出括号时就释放up2{unique_ptr<Foo> up2(new Foo);}cout << "Example constructor(3)...\n";D d;{//d是类D的对象,up3的构造函数以d作为了参数,所以会调用D的复制构造函数unique_ptr<Foo, D> up3(new Foo, d);}{//传递的参数已经是引用了,不需要像上面一样再调用复制构造unique_ptr<Foo, D&> up4(new Foo, d);}//3个数组,构造调用3次,析构也会调用3次cout << "Example array constructor...\n";{unique_ptr<Foo[]> up(new Foo[3]);}return 0;
}

测试结果:

$ ./a.out
Example constructor(1)...
Example constructor(2)...
Foo default ctor
~Foo dtor
Example constructor(3)...
D default ctor
Foo default ctor
D copy ctor
D is deleting Foo
~Foo dtor
Foo default ctor
D is deleting Foo
~Foo dtor
Example array constructor...
Foo default ctor
Foo default ctor
Foo default ctor
~Foo dtor
~Foo dtor
~Foo dtor

2. 管理器 release/reset/swap

这几个管理去都比较简单,直接看代码即可:

struct Foo {int val;Foo() {cout << "Foo default ctor\n";}Foo(int _val):val(_val) {cout << "Foo int val\n";}~Foo() {cout << "~Foo dtor\n";}
};//删除器
struct D {D() {cout << "D default ctor\n";}D(const D&) {cout << "D copy ctor\n";}void operator()(Foo* p) const {cout << "D is deleting Foo\n";delete p;};
};int main(void)
{//1.release 释放被管理对象cout << "Example 1, release....\n";//创建被管理对象的指针unique_ptr<Foo> up(new Foo());//使用release,释放被管理对象的所有权,给另外一个指针(另外一个指针并非智能指针)Foo *fp = up.release();assert (up.get() == nullptr);cout << "Foo is no longer owned by unique_ptr...\n";delete fp;//2.reset 替换被管理对象//与release的区别在于,这里会先析构掉旧的智能指针,然后创建新的cout << "Example 2, reset....\n";unique_ptr<Foo> up2(new Foo());cout << "new Foo, start reset--------\n";up2.reset(new Foo());cout << "new Foo, reset done--------\n";//3.swap,用来交换 *this和另一unique_ptr对象other所管理的内容//void swap(unique_ptr& other) noexcept;cout << "Example 3, swap....\n";unique_ptr<Foo> up3a(new Foo(1));unique_ptr<Foo> up3b(new Foo(2));up3a.swap(up3b);cout << "up3a->val:" << up3a->val << endl;cout << "up3b->val:" << up3b->val << endl;return 0;
}

测试结果:

Example 1, release....
Foo default ctor
Foo is no longer owned by unique_ptr...
~Foo dtor
Example 2, reset....
Foo default ctor
new Foo, start reset--------
Foo default ctor
~Foo dtor
new Foo, reset done--------
Example 3, swap....
Foo int val
Foo int val
up3a->val:2
up3b->val:1
~Foo dtor
~Foo dtor
~Foo dtor

3. 观察器 get/get_deleter/operator bool

直接看代码:

struct Foo {int val;Foo() {cout << "Foo default ctor\n";}Foo(int _val):val(_val) {cout << "Foo int val\n";}~Foo() {cout << "~Foo dtor\n";}
};//删除器
struct D {void test_func(void) {cout << "test_func" << endl;};D() {cout << "D default ctor\n";}D(const D&) {cout << "D copy ctor\n";}void operator()(Foo* p) const {cout << "D is deleting Foo\n";delete p;};
};
int main(void)
{unique_ptr<string> up1(new string("hello"));string *p1 = up1.get();cout << *p1 << endl;cout << "Example 2, get_deleter ....\n";//2.get_deleter返回被管理对象的删除器unique_ptr<Foo, D> up2(new Foo, D());D& d = up2.get_deleter();d.test_func();//3.operator bool,就是将指针对象进行运算符重载,重载成一个bool量cout << "Example 3, operator bool ....\n";if ((up1) && (up2)){cout << true << endl;}return 0;
}

测试结果:

$ ./a.out
Example 1, get....
hello
Example 2, get_deleter ....
D default ctor
Foo default ctor
D copy ctor
test_func
Example 3, operator bool ....
1
D is deleting Foo
~Foo dtor

2.3 auto_ptr和unique_ptr对比

unique_ptr作为auto_ptr的升级版,优势具体在哪里,我们做实验来进行对比。

1.赋值运算

差异:unique_ptr比auto_ptr的赋值运算更为安全;

  • auto_ptr的赋值运算符等效于调用 reset(r.release()) ,实际上是把智能指针的所有权转移,原智能指针被release之后已经变成nullptr;
  • unique_ptr 不允许直接赋值,需要使用移动赋值

1.autoptr例子:

//1.auto_ptr的例子,直接赋值
class people
{public:people() {cout << "people()" << endl;}people(string name) {this->name = name;cout << "people(string name)" << endl;}~people() {cout << "~people()" << endl;}void print_name(void){cout << "name: " << this->name << endl;}
private:string name;};void test_func(auto_ptr<people> ptr)
{ptr->print_name();
}int main(void)
{//1.测试1:直接使用=赋值运算符,等效于调用 reset(r.release())auto_ptr<people> p1(new people("jason"));auto_ptr<people> p2 = p1;p2->print_name();p1->print_name(); //这里发生了core dumped,发生coredump的原因是此时p1已经是Nullptr,被reset(nullptr)了
}

测试结果:

$ ./a.out
people(string name)
name: jason
Segmentation fault (core dumped)

2.unique_ptr例子:

struct Foo {int val;Foo() {cout << "Foo default ctor\n";}Foo(int _val):val(_val) {cout << "Foo int" << this->val <<endl;}~Foo() {cout << "~Foo dtor\n";}void print_val() {cout << "val is" << this->val << endl;}
};//删除器
struct D {void test_func(void) {cout << "test_func" << endl;};D() {cout << "D default ctor\n";}D(const D&) {cout << "D copy ctor\n";}void operator()(Foo* p) const {cout << "D is deleting Foo\n";delete p;};
};
int main(void)
{unique_ptr<Foo> up(new Foo(4));// unique_ptr<Foo> up2 = up; //unique_ptr不允许直接赋值,error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Foo; _Dp = std::default_delete<Foo>]’unique_ptr<Foo> up2 = move(up);up2->print_val();return 0;
}

测试结果:

2.作为函数参数的差异

差异:

  • auto_ptr作为函数参数时,只能传引用,而不能传值,传值会导致一次指针的赋值,导致指针变空;
  • unique_ptr作为函数参数时,既能传引用,又能传值;

1.autoptr例子:
测试代码:
使用 test_func 时,后面会发生coredump,使用 test_func_re()就可以,原因是因为传值作为参数时,会产生一个临时对象来接受参数,其实又回到第一个差异,赋值的问题。

class people
{public:people() {cout << "people()" << endl;}people(string name) {this->name = name;cout << "people(string name)" << endl;}~people() {cout << "~people()" << endl;}void print_name(void){cout << "name: " << this->name << endl;}
private:string name;};void test_func(auto_ptr<people> ptr)
{ptr->print_name();
}void test_func_re(auto_ptr<people>& ptr)
{ptr->print_name();
}int main(void)
{//2.测试2:auto_ptr作为函数的参数auto_ptr<people> p1(new people("jason"));// test_func(p1); //在这里就发生了析构// p1->print_name(); //core dumpedtest_func_re(p1);
}

测试结果:

2.unique_ptr例子:

struct Foo {int val;Foo() {cout << "Foo default ctor\n";}Foo(int _val):val(_val) {cout << "Foo int" << this->val <<endl;}~Foo() {cout << "~Foo dtor\n";}void print_val() {cout << "val is" << this->val << endl;}
};//删除器
struct D {void test_func(void) {cout << "test_func" << endl;};D() {cout << "D default ctor\n";}D(const D&) {cout << "D copy ctor\n";}void operator()(Foo* p) const {cout << "D is deleting Foo\n";delete p;};
};void test_arg_func(unique_ptr<Foo> ptr)
{ptr->print_val();
}
int main(void)
{unique_ptr<Foo> up(new Foo(4));test_arg_func(move(up));return 0;
}

c++系列 —— 智能指针auto_ptr和unique_ptr相关推荐

  1. C++的智能指针auto_ptr、unique_ptr源码解析

    C++的智能指针auto_ptr.unique_ptr源码解析 1.前言 2.源码准备 3.源码解析 3.1.auto_ptr解析 3.2.unique_ptr解 3.3.unique_ptr的一个偏 ...

  2. 学习笔记 | c++中的smart pointer四个智能指针 shared_ptr、unique_ptr、weak_ptr、auto_ptr

    c++中的smart pointer四个智能指针 C++里面的四个智能指针: auto_ptr, shared_ptr, weak_ptr, unique_ptr其中后三个是c++11支持,并且第一个 ...

  3. C++ 智能指针(二) std::unique_ptr

    C++ STL智能指针系列: C++ 智能指针(一) std::auto_ptr C++ 智能指针(二) std::unique_ptr C++ 智能指针(三) std::shared_ptr C++ ...

  4. 智能指针shared_ptr、unique_ptr、weak_ptr

    智能指针 智能指针解决的问题 智能指针分类 shared_ptr 内存模型图 shared_ptr示例 shared_ptr含义 shared_ptr基本用法及常用函数 常用函数 智能指针的构造,初始 ...

  5. C++智能指针shared_ptr、unique_ptr以及weak_ptr

    目录 shared_ptr类 shared_ptr和unique_ptr都支持的操作 shared_ptr独有的操作 make_shared函数 shared_ptr自动销毁所管理的对象 由普通指针管 ...

  6. C++——智能指针——auto_ptr、shared_ptr、unique_ptr

    1.4.智能指针 智能指针是行为类似于指针的类对象. C++11 中提供了三种智能指针,使用这些智能指针时需要引用头文件 : ·shared_ptr; ·unique_ptr; ·auto_ptr; ...

  7. C++ -- 智能指针 auto_ptr,unique_ptr,shared_ptr的简单实现和原理

    一,为什么需要智能指针 智能指针是一种预防型的内存泄漏的解决方案.由于C++没有垃圾回收器机制,所以每次new出来的资源都需要手动的delete,如果没有手动释放,就会造成资源泄漏等问题.因此,为了避 ...

  8. 智能指针——auto_ptr

    1. 开篇 C++里面的四个智能指针:auto_ptr.unique_ptr.shared_ptr.weak_ptr,其中后三个是C++11支持,而这个auto_ptr已经被C++11弃用.但auto ...

  9. c++11 智能指针 (std::unique_ptr)(一)

    定义于头文件 <memory> template<class T,class Deleter = std::default_delete<T>> class uni ...

最新文章

  1. TCP/IP 协议简单分析
  2. joda jar日期处理类的学习
  3. 在Linux下使用screen使用退出远程终端后程序依然自动进行
  4. Android问题-DelphiXE8安装后编译Android提示SDK无法更新问题(XE10也可以解决)
  5. 算法及shell脚本编程基础
  6. java query object_java queryforobject
  7. 篇章级关系抽取(Doc-RE)论文列表整理
  8. 选课系统服务器,选课系统概要设计
  9. Python源码保护
  10. 总结 工作法(时间管理+复盘)
  11. ajax 发送 put 请求
  12. 06年你看过《武林外传》吗?如果看过,就明白什么叫物是人非
  13. docker网络原理及cgroup
  14. glib安装详细教程
  15. 两个程序员(Chris和Steve)的故事
  16. 【JZOJ 5498】 大佬的难题
  17. 2018年6月编程语言tiobe排行
  18. 网页js识别移动端几种方法
  19. 工具分享 | linemap-快速绘制山峦地图的R可视化包介绍
  20. IE安装HttpWatch使用教程HttpWatch协议抓取包不显示解决办法

热门文章

  1. Fragment在ViewPager中的生命周期
  2. win10远程桌面连接计算机密码错误,win10远程桌面连接不上怎么办?windows10远程桌面连接不上的解决方法...
  3. leetcode刷题记录2:进度64, 2021.10.23
  4. mac 安装nginx与switchhosts 并在微信开发者工具调试h5项目
  5. Android 第三方应用跳转到qq进行聊天(qq咨询)
  6. 周围神经系统的分类和分布,周围神经系统的简称
  7. 移动端扫描vin码(车架号)识别
  8. abp框架学习笔记(三)--Angular和前端
  9. 基于DDD的现代ASP.NET开发框架--ABP系列文章总目录
  10. Linux下给wps增加桌面图标