12.1.1shared_ptr类

  1. 动态内存
  2. 动态内存与智能指针
  3. Shared_ptr类
  4. Make_shared函数
  5. Shared_ptr的拷贝和赋值
  6. Shared_ptr自动销毁所管理的对象
  7. Shared_ptr还会自动释放相关联的内存
  8. 使用了动态生存期的资源的类
  9. 定义StrBlob类
  10. StrBlob构造函数
  11. 元素访问成员函数
  12. StrBlob的拷贝、赋值和销毁

自由空间

动态分配

New

Delete

智能指针

引用计数

Shared_ptr

Unique_ptr

Weak_ptr

Shared_ptr<string> p1;

Shared_ptr<list<int>> p2;

Shared_ptr<int> p3 = make_shared<int>(42);

Shared_ptr<string> p4 = make_shared<string>(10, ‘9’);

Auto p6 = make_shared<vector<string>>();

Auto p = make_shared<int>(42);

Auto q(p);

析构函数

1. 动态内存

1)静态内存或栈内存。静态内存存储static的对象数据成员,栈内存用来保存非static成员。

2)分配在静态内存或栈内存中的对象由编译器自动创建和销毁。

3)栈对象仅在程序块运行时才存在,static对象再使用之前分配,程序结束时销毁

4)还有内存池。这部分内存被称为自由空间或堆。堆存储动态分配的对象,在程序运行时分配的对象。动态对象的生成期由程序控制,所以需要用代码显示销毁。

Note:动态内存使用是有必要的,但是正确的管理棘手的

2. 动态内存与智能指针

1)动态内存的管理通过一对运算符完成:new和delete。

2)New在动态内存中为对象分配空间并返回一个指向该对象的指针

3)Delete接受一个动态对象的指针,销毁该对象释放与之关联的内存

4)但是管理困难,会忘记销毁,会造成引用非法内存的指针,所以需要用标准库提供的两种智能指针。智能指针与常规指针类似,区别于自动销毁负责的对象。而这两种智能指针的区别又在于管理底层指针的方式。

5)Shared_ptr允许多个指针指向同一个对象;unique_ptr则独占所指向的对象。

6)还有一个weak_ptr伴随类,弱引用,指向shared_ptr所管理的对象。在memory头文件

3. Shared_ptr类

1)是模板,需提供指向的类型,在尖括号中

2)默认初始化为一个空指针

3)使用与普通指针类似。

4. Make_shared函数

1)在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。

2)类似容器的emplace成员,可以用参数来构造给定类型的对象。

3)Auto更简单的创建shared_ptr

5. Shared_ptr的拷贝和赋值

1)拷贝或赋值时,每个shared_ptr都会基类有多少个其它shared_ptr指向相同的对象

2)有个关联的计数器,引用计数。拷贝或传参,返回值,赋值右侧,计数器会递增。赋值左侧或销毁时(离开作用域)计数器递减

Note:标准库实现记录,关键是它能在恰当时候自动释放对象

6. Shared_ptr自动销毁所管理的对象

1)当最后一个shared_ptr对象被销毁时,shared_ptr类会自动销毁此对象。是通过析构函数

2)析构函数会递减引用计数,若为0就会销毁对象。并释放它占用的内存

7. Shared_ptr还会自动释放相关联的内存

1)嗯,就是5.1说的

2)在最后一个shared_ptr销毁前内存都不会释放,若有不在需要的shared_ptr,程序正确执行,但浪费内存。应该用erase删除那些不再需要的shared_ptr元素

3)如:shared_ptr存放在一个容器中,随后重排了容器,不再需要某些元素,只需要部分,所以需要erase删除不再需要的元素

8. 使用了动态生存期的资源的类

使用动态内存的原因:

1)程序不知道自己需要使用多少对象

2)程序不知道所需对象的准确类型

3)程序需要在多个对象间共享数据

原因对应的具体发生在什么情况:

1)原因1:容器类

2)原因2:继承时 基类指针需指向子类指针才会发生动态绑定

3)原因3:就是shared_ptr,可以自定义一个类有动态内存的对象来记录多少个共享对象,实现类似shared_ptr功能

4)我们所用的类中,分配的资源都与对象对象生存期一致。如:vector赋值给一个vector,其实拷贝vector中的元素,销毁一个vector不影响另一个vector。

我们可以实现类,分配的资源具有与源对象相独立的生存期。如:一个类赋值给一个类对象后,左侧对象引用右侧对象的值,当右侧对象销毁,这个值在内存中不该销毁。

Note:使用动态内存是允许多个对象共享相同的状态。

9. 定义StrBlob类

1)实现这个类上面所说的不是拷贝,是引用,不会删除底层数据

2)使用shared_ptr管理动态分配的vector…等一些方法

10. StrBlob构造函数

1) 用make_shared初始化shared_ptr,参数可以是一个列表initializer_list

11. 元素访问成员函数

12. StrBlob的拷贝、赋值和销毁

1)  使用默认版本。Shared_ptr会自动处理什么时候删除底层数据。构造是拷贝引用不是拷贝底层数据

12.1.2直接管理内存

  1. 直接管理内存
  2. 使用new动态分配和初始化对象
  3. 动态分配的const对象
  4. 内存耗尽
  5. 释放动态内存
  6. 指针值和delete
  7. 动态对象的生存期直到被释放时为止
  8. Delete之后重置指针值
  9. 这只是提供了有限的保护

New

Delete表达式

Int *p1 = new int;

Int *p2 = new int(1034);

Vector<int> *pv = new vector<int>{1,2,3};

Int *p3 = new int();

Auto p4 = new auto(obj);

Auto a5 = new auto{a,b,c};//错误

Const int *pci = new cons tint(1024);

Int *p1 = new (nothrow)int;

定位new

delete p1;

Foo* factory(T arg)

{

Return new Foo(arg);

}

Void use_factory(T arg)

{

Foo *p = factory(arg);

Delete;

}

空悬指针

Auto q = p;

Delete p;

P = nullptr;

1. 直接管理内存

1)New分配内存,delete释放new分配的内存

2)不能依赖类对象拷贝 赋值和销毁操作的任何默认定义

Note:最好用智能指针分配内存

2. 使用new动态分配和初始化对象

1)自由分配的内存是无名的,因此new无法为其分配的对象命名,而是返回一个指向该对象的指针

2)默认下动态分配的对象是默认初始化的,内置类型或组合类型的对象的值将是未定义的,而类类型对象将用默认构造函数进行初始化

3)可以使用直接初始化方式、列表初始化、传统的构造方式

int *p3 = new int(10);// 直接

string *pstr2 = new string(10, '9');//构造

vector<int> *pv1 = new vector<int>{ 1,2,3 };//列表

4)值初始化,类型名后加一个括号。

string *pstr3 = new string();// 与默认构造函数的默认初始化一样

int *p4 = new int();// 内置类型 有区别,默认初始化为未定义,值初始化良好定义的值。

5)Note:处于与变量初始化相同的原因,对动态分配的对象进行初始化通常是个好主意

6)auto推断类型,只有单一初始化器时才可以。若obj是int,p1是int*

3. 动态分配的const对象

1)必须初始化。一个定义了默认构造函数的类类型,其const动态对象可以隐式初始化,而其他类型的对象必须显式初始化。分配的对象是const的,返回的指针是一个指向const的指针

const int *p7 = new const int;// 未定义 也可以?14?

const string *p8 = new const string;// 隐式初始化

4. 内存耗尽

1)堆内存耗尽可能发生。耗光了再new会抛bad_alloc。可以改变new的方式阻止异常

2)定位new,若异常不能new出来则返回一个空指针。Bad_alloc与nothrow在头文件new中

int *p9 = new (nothrow) int; // 定位new 允许我们向new传递额外的参数

5. 释放动态内存

1)释放动态内存 将内存归还给系统,两个动作,销毁给定的指针指向的对象,释放对应的内存

delete p9;// 指向的可以是空指针 或者 动态分配的对象

6. 指针值和delete

1)删除的对象空指针 或者 动态分配的内存

2)若多次删除同一个指针值或不是new分配的内存,未定义

3)编译器不能分布一个指针指向静态的还是动态的对象。同样也不能分辨一个指针所指的内存是否已经被释放了。Delete表达式编译器会通过,尽管他们是错误的

4)编译通过,但是却是错误的

7. 动态对象的生存期直到被释放时为止

1)内置指针管理的动态对象,直到被显式释放之前都是存在的

2)函数返回一个指针,那么调用者要记得释放,但是调用者经常忘记释放

3)即使所在作用域的函数结束了,指针会销毁 但是指针所值得内存不会销毁不会自动释放

Note:动态内存的管理非常容易出错

1)  内存泄漏,当内存很多未delete。内存泄漏在程序运行很长一段时间后才能检测这种错误,查找内存泄漏错误是非常困难的。

2)  使用已经释放掉的对象。有时在释放内存后将指针置位空,有时可以检测出这种错误

3)  同一个内存释放两次。

8. Delete之后重置指针值

1)delete一个指针后,指针值无效。但很多机器上指针仍然保存值已经释放了的动态内存的地址。空悬指针:指向一块曾经保存数据对象但现在已经无效的内存的指针

2)具有未初始化指针所有缺点

3)解决:

1. 在即将离开作用域时释放掉它所关联的内存

2. 若想保留指针,在delte之后将nullptr赋予指针。

9. 这只是提供了有限的保护

1)上面的解决方法2。单对这个指针有效,若多个指针指向相同的内存,那么对其他指针是没有作用的。

2)删除其一指针释放内存,再赋为nullptr使其安全,但是其它指针却还是未定义的

int *p15 = new int(12);

int *p16 = p15;// 指向相同的内存

delete p15;// p

p15 = nullptr;// 指出p不再绑定到任何对象,但p16并不为nullptr为空悬指针,也需要指向nullptr

12.1.3shared_ptr和new结合使用

  1. shared_ptr和new结合使用
  2. 不要混合使用普通指针和智能指针
  3. 也不要使用get初始化另一个智能指针或为智能指针赋值
  4. 其他shared_ptr操作

Shared_ptr<int> p1(new int(42));

Shared_ptr<int> clone(int p){

Return shared_ptr<int>(new int(p));

}

Void process(shared_ptr<int> ptr)

Shared_ptr<int> p(new int(42));

Process(p);

Int I = *p;

Int *x(new int(1024);

Process(shared_ptr<int>(x));//合法,但内存会被释放

Shared_ptr<int>p(new int(42));

Int *q = p.get();

{

Shared_ptr<int>(q);

}

Int foo = *p;//未定义

p.reset(new int(1024));

1. shared_ptr和new结合使用

1)若不初始化一个智能指针,就被初始化为一个空指针

2)我们还可以用new返回的指针来初始化智能指针

3)智能指针的构造函数是explicit ,就是说,必须用直接初始化,不能用赋值初始化 =

Share_ptr<int> p1 = new int(1024);//no

4)初始化智能指针的普通指针必须指向动态内存,智能指针用delete删除它,但是可以关联指向其它类型的资源的指针上,需提供自己delete的操作

2. 不要混合使用普通指针和智能指针

1)协调对象的析构,但仅限于自身的拷贝,避免无意中将同一块内存绑定到多个独立创建的shared_ptr上。

2)用new不能协调,用make_shared可以协调

3)例子,给process传递一个临时shared_ptr指向内置指针

Int *x(new int(1024);

Process(shared_ptr<int>(x));//合法,但x内存会被释放

表达式是将这个临时对象传给了形参才结束,创建时为1,传递给形参计数为2,表示式结束销毁为1,形参结束为0,所以会销毁指针x的内存

4)X会变成空悬指针,未定义状态。

5)把内置指针交给智能指针操作,就不要使用内置指针访问内存的好,无法知道什么时候被销毁

3. 也不要使用get初始化另一个智能指针或为智能指针赋值

1)Shared_ptr的Get函数返回一个内置指针,指向智能指针管理的对象。是为了只能只用内置指针不能使用智能指针而设计的。Get返回的指针不能用delete此指针

2)若将get返回的指针给另一个shard_ptr,这两个shared_ptr并没有关联,于是若另一个shared_ptr被销毁那么原shared_ptr指向的内存也被销毁,变为空悬指针,产生未定义行为。而且原指针被销毁,同一块内存delete两次。

Note:使用get得到的内置指针最好不要delete,不要用get初始化另一个智能指针或者为另一个智能指针赋值

4. 其他shared_ptr操作

p7.reset(new int(19));

1)用reset来将一个新的指针赋予一个shared_ptr,经常与unique一起使用

2)Reset将会销毁原来p7的内存并且指向新内存,若没有指向新内存,让p置位空

3)Unique判断自己是否唯一用户,是的话可以随意改变自身的值

12.1.4智能指针和异常

  1. 智能指针和异常
  2. 智能指针和哑类
  3. 使用我们自己的操作

删除器

Void f(){

Shared_ptr<int> sp1(new int(42));

// 抛出异常,并未在f中捕获,则中断下面代码由上查找

}

Void f(){

Int *ip = new int(42);

// 抛出异常,并未在f中捕获,则中断下面代码由上查找

Delete ip;//未执行

}

Void f(){

Connection c ;

//若没有显式调用销毁代码,无法关闭c了

}

void deletefunc(connection*p)

{

Disconnect(*p);

}

Void f(destination &d){

Connection c = connect(&d);

Shared_ptr<connection> p(&c,end_connection);

}

1. 智能指针和异常

1)若程序块过早结束(正常处理或发生异常不在本程序块捕获,向上查找),局部对象会被销毁,但是若是动态分配的对象则需要看什么指针类型

2)若自己管理动态内存,在最后面进行delete,delete代码不执行,指向的内存将不会释放,若智能指针函数结束时还是会执行

2. 智能指针和哑类

1)很多类都定义了析构函数清理对象使用的资源,不是所有类都有这种良好的定义,特别是为c和c++两种语言设计的类,通常需要显式的释放。就是例如内置类型对象离开作用域自动销毁,但是这种需要显式释放的对象没有析构函数,则需要手动删除,就是delete 指针 一样要显式调用关闭的代码。

2)但是如果我们忘记显式调用则无法关闭和释放了。

3. 使用我们自己的操作

1)可以使用shared_ptr来正确释放。但是shared_ptr默认是指向动态内存,所以需要传递指针类型。需定义一个函数来作为删除器。必须能够完成对shared_Ptr中保存的指针进行释放的操作

2)在创建shared_ptr时指定这个删除器方法就行。就是调用具体的方法来释放

3)可用lambda也可以用函数自定义删除

注意:智能指针陷阱

1)不适用相同的内置指针初始化多个智能指针

2)不delete get() 返回的指针

3)不适用get() 初始化或reset另一个智能指针

4)适用get()返回的指针,记住最后一个对象的智能指针销毁后,指针变为无效

5)如果使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器

12.1.5unique_ptr

  1. unique_ptr
  2. 传递unique_ptr参数和返回unique_ptr
  3. 向unique_ptr传递删除器

Unique_ptr<double> p1;

Unique_ptr<int> p2(new int(42));

Unique_ptr p2(p1);//错误 不支持拷贝或赋值

Unique_ptr<string> p2(p1.release());

P2.reset(p3.release());

P2.release();

Auto p = p2.release();

Unique_ptr<int> clone(int p){

Return unique_ptr<int>(new int(p));//或者局部对象

}

Unqiue_ptr<objT,delT> p(new objT,fcn);

Void f(destination &d){

Connection c = connect(&d);

Unique_ptr<connection,decltype(end_connection)*

> P(&c,end_connection);

}

1. unique_ptr

1)只能有一个unique_ptr指向一个给定对象。

2)没有类似make_ptr的标准库函数返回一个unique_ptr。定义时需要绑定到一个new返回的指针上

3)必须使用直接初始化方式unique_ptr<string> p1(new string("Stegosaurus"));

4)由于只能有一个unique_ptr拥有它指向的对象,所以不支持普通的拷贝或赋值操作

5)虽然不能拷贝或赋值unique_ptr,但可以通过使用releas或reset将指针的所有权从一个非const unique_ptr转移到另一个unique。

6)Release()会将对象置为空,转移给新的unique_ptr,若没有接受的unique_ptr会导致无法销毁这个内存了。Reset是先销毁左侧的指向内存再指向新的内存。

2. 传递unique_ptr参数和返回unique_ptr

1)但是将要删除unique_ptr时编译器允许拷贝或者赋值操作

Note:向后兼容:auto_ptr,以前版本,不要用了

3. 向unique_ptr传递删除器

1)默认是delete释放它指向的对象。

2)必须在尖括号中unique_ptr指向类型之后提供删除器类型。在创建或reset一个这种unique_ptr类型对象时,必须提供一个指定类型的可调用对象(删除器)

3)Decltype指明函数指针类型,但需要加上*,表示我们在使用该类型的一个指针。

12.1.6weak_ptr

  1. weak_ptr
  2. 核查指针类
  3. 指针操作

Auto p = make_shared<int>(42);

Weak_ptr<int> wp();

Wp.lock();

1. weak_ptr

1)不控制所指向对象生存期的智能指针,指向shared_ptr管理的对象,不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向,还是会释放,所以叫弱

2. 伴随指针

3)Weak_ptr不能直接访问对象,Lock返回一个指向共享对象的shared_ptr。只要此shared_ptr存在,所指向的底层对象也一直存在

2. 核查指针类

1)安全性更高,通过lock判断对象是否存在再进行操作

3. 指针操作

1)定义StrblobPtr类

2)就是lock获取shared_ptr指向的对象,就是指针需要解引用操作。

《C++ Primer 第5版》-12.1动态内存与智能指针-康奈尔笔记相关推荐

  1. 【C++ Primer 第5版 笔记】第12章 动态内存与智能指针

    转载:http://blog.csdn.net/wwh578867817/article/details/41866315 第 12 章 动态内存 与 智能指针 静态内存 用来保存:(1)局部stat ...

  2. 12.1 动态内存与智能指针(2)

    今天继续学习12.1节时,从练习12.17中发现了一个问题. 首先摘录教材中的原话--一个unique_ptr"拥有"它所指向的对象.与shared_ptr不同,某个时刻只能有一个 ...

  3. 2.5w字长文爆肝 C++动态内存与智能指针一篇搞懂!太顶了!!!

    动态内存与智能指针 1.动态内存与智能指针 2.shared_ptr类 2.1.make_shared函数 2.2.shared_ptr的拷贝和赋值 2.3.shared_ptr自动销毁所管理的对象 ...

  4. C++ Primer 5th笔记(chap 12 动态内存)智能指针概述

    1. 对象的生存期 内存类型 定义 分配和销毁时机 全局对象 程序启动时分配,程序结束时销毁 静态内存 局部static对象类static数据成员 第一次使用时分配,程序结束时销毁 栈内存 定义在函数 ...

  5. C++相关:动态内存和智能指针

    前言 在C++中,动态内存的管理是通过运算符new和delete来完成的.但使用动态内存很容易出现问题,因为确保在正确的时间释放内存是及其困难的.有时候我们会忘记内存的的释放,这种情况下就会产生内存泄 ...

  6. 【Smart_Point】动态内存与智能指针

    动态内存 动态内存使用的三种原因 程序不知道自己需要多少对象 程序不知道所需对象的准确类型 程序需要在多个对线之间共享数据 文章目录 动态内存 动态内存使用的三种原因 实例1: Exercise 12 ...

  7. C++知识点34——动态内存与智能指针

    一.动态内存 动态内存所在的位置在堆区,由程序员手动分配并手动释放,而不像栈内存由系统分配和自动释放 C++通过new运算符为对象在堆上分配内存空间并返回该对象的地址,并用delete运算符销毁对象并 ...

  8. 【Smart_Point】动态内存与智能指针实战:文本查询程序(设计set,map,智能指针的应用)

    文章目录 Cpp读入结构性数组 文本查询程序 文本查询程序本版1 Cpp读入结构性数组 #include<sstream> #include<iostream> #includ ...

  9. C++ Primer 第五版 第6章 6.7——函数指针习题答案

    理论请参考:C++ Primer 第五版 第6章 6.7--函数指针阅读笔记 目录 6.7 函数指针习题答案 6.54 6.55 6.56 6.7 函数指针习题答案 6.54 vector是指向该函数 ...

  10. C++ Primer 第五版 第6章 6.7——函数指针阅读笔记

    习题答案请参考:C++ Primer 第五版 第6章 6.7--函数指针习题答案 目录 6.7 函数指针 使用函数指针 返回指向函数的指针 6.7 函数指针 声明一个函数指针,只需要用指针替代函数名即 ...

最新文章

  1. python中,time、calendar、datetime
  2. python3 报错 TypeError: load() got an unexpected keyword argument ‘encoding‘ 解决方法
  3. 学python需要学数据库吗-学习Python爬虫前,你必须知道的一些工具!
  4. 20165203第四周考试
  5. 阿里云服务器CentOS6.9 nexus私服使用
  6. 用php实现动态产生xml文件以及从xml文件中抽取数据转化成html的
  7. 相机标定中部分疑问和注意事项
  8. Oracle START WITH ... CONNECT BY PRIOR 带条件会有重复
  9. java怎样将多个list写入txt中并且不覆盖原有数据_java容器,面试必备知识点整理!...
  10. java的robot一直按住某个键_SHIFT键在CAD中使用技巧
  11. SAP Basic T-Code
  12. WCF 项目应用连载[8] - 绑定、服务、行为 大数据传输与限流 - 下 (ServiceThrottlingAttribute)...
  13. 英语面试(自我介绍,电话面试..)
  14. 全球及中国冻干菠萝行业市场运行模式及未来发展前景分析报告2022-2028年
  15. 苹果计算机使用方法,不知道这10个Mac使用技巧,别说你会用苹果电脑
  16. 什么是windows的域(Domain)?
  17. 图像处理的一些相关知识(Related knowledge for IQA)
  18. benchmark测试
  19. mysql5.1.40.jrp_1.原生态JDBC编程中的问题总结
  20. 布袋除尘器过滤风速多少_布袋除尘器-布袋除尘器过滤风速是指的什么?-宏大除尘设备...

热门文章

  1. Springboot结合ESAPI——配置XSS防御过滤
  2. Oracle建表规范
  3. u盘文件名乱码linux,U盘文件名乱码的原因和解决办法
  4. 半导体物理学习笔记(一)
  5. 微服务社交平台项目【十次方】(二)-API文档与模拟数据接口
  6. 关于伪分布式hadoop集群及HBase安装的一些记录(基于林子雨老师的大数据软件安装和基础编程)
  7. 机器学习(3)--LR算法
  8. 牛顿三次插值 matlab,matlab 牛顿插值法 三次样条插值法[行业二类]
  9. 制作一个简单HTML静态网页(HTML+CSS)
  10. php搭建聊天室,php聊天室_用PHP MySQL搭建聊天室