C++学习:动态内存和智能指针

16 篇文章 1 订阅

动态内存

静态内存用来保存局部 static 对象、类 static 数据成员、定义在任何函数之外的变量。static 对象在使用之前分配,在程序结束后销毁。

栈内存用来保存定义在函数内的非static对象。对于栈对象,仅在其定义的程序块运行时才存在。

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

动态内存用来存储动态分配的对象,程序在运行时分配的对象,动态对象的生存期由程序来控制,当不再使用时,必须显式地销毁它们。

在C++中,动态内存的管理是一对运算符完成的:newdelete

  1. new:在动态内存中为对象分配控件并返回一个指向该对象的指针;
  2. delete:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。

动态内存管理经常会出现两种问题:

  1. 忘记释放内存,造成内存泄漏
  2. 尚有指针引用内存的情况下就释放了该内存,造成引用非法内存
  3. 同一块内存释放两次,第一次delete正常释放空间,第二次delete将可能破坏自由空间。

为了更加容易、安全地使用动态内存,新的标准库引入了智能指针。智能指针的行为类似常规指针,重要的区别在于智能指针负责自动释放所指向的对象

智能指针

智能指针是一个类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄露。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源。智能指针包含在头文件****中。

shared_ptr

允许多个指针指向同一个对象;该类型智能指针在实现上采用 引用计数机制,每当多一个指针指向该对象时,指向该对象的所有智能指针内部的引用计数加1,当有一个shared_ptr指针放弃了堆内存的“使用权”,引用计数减1,此时并不影响其他指向同一堆内存的shared_ptr指针,只有引用计数为0时,堆内存才会被释放。

  • 智能指针将一个计数器与类指向的对象相关联,引用计数器跟踪共有多少个类对象共享同意指针;
  • 每次创建类的新对象时,初始化指针并将引用计数置为1;
  • 当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;
  • 对一个对象进行赋值时,赋值操作符减少左操作数所指向的引用计数(如果引用计数减至0,则删除对象),并增加右操作数所指对象的引用计数;
  • 调用析构函数时,构造函数减少引用计数,若引用计数减为0,则删除基础对象。

shared_ptr和unique_ptr都支持的操作

操作功能说明
shared_ptr sp空智能指针,可以指向类型为T的对象
unique_ptr up空智能指针,可以指向类型为T的对象
p将p用作一个条件判断,若p指向一个对象,则为true。
*p解引用p,获得它指向的对象
p->mem等价于(*p).mem
p.get()返回p中保存的指针,若智能指针释放了其对象,返回的指针所指向的对象也就消失了
swap(p, q)交换p和q中的指针
p.swap(q)交换p和q中的指针

shared_ptr独有的操作

操作功能说明
make_shared(args)返回一个shared_ptr,指向 一个动态分配的类型为T的对象,使用args初始化此对象
shared_ptrp(q)p是shared_ptr q的拷贝;此操作会递增q中的计数器。q中的指针必须能转化为T*。
p = qp和q都是shared_ptr,所保存的指针必须能相互转换。此操作会递减p的引用计数,递增q的引用计数;若p的引用计数为0,则将其管理的原内存释放。
p.unique()若p.use_count()为1,返回true;否则返回false
p.use_count()返回与p共享对象的智能指针数量,可能很慢,主要用于调试

用法举例

shared_ptr<string> p1;
shared_ptr<int> p2 = make_shared<int>(42);
auto p3 = make_shared<vector<int>>();

unique_ptr

独占所指向的对象,采用独享所有权语义,一个非空的unique_ptr总是拥有它所指向的资源。
转移一个unique_ptr将会把所有权全部从源指针转移给目标指针,源指针被置空;所以unique_ptr不支持普通的拷贝和赋值操作不能用在STL容器中;如果对一个unique_ptr进行拷贝,那么拷贝结束后,这两个unique_ptr都会指向相同的资源,造成在结束时对同一内存指针多次释放而导致程序崩溃。

unique_ptr的操作

操作功能说明
unique_ptr u1空unique_ptr,可以指向类型为T的对象。u1会使用delete来释放它的指针;u2会使用一个类型为D的可调用对象来释放它的指针。
unique_ptr<T, D> u2
unique_ptr<T, D> u(d)空unique_ptr,指向类型为T的对象,用类型为D的对象d代替delete
u = nullptr释放u指向的对象,将u置为空
u.release()u放弃对指针的控制权,返回指针,将u置空
u.reset()释放u指向的对象
u.reset(q)如果提供了内置指针q,令u指向这个对象;否则将u置空
u.reset(nullptr)将u置空

weak_ptr

弱引用。引用计数机制会存在一个问题:互相引用成环,即环形引用,这样两个指针指向的内存都无法释放,需要使用weak_ptr打破环形引用。
weak_ptr作为弱引用,是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,即只引用不计数
如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放,所以weak_ptr不保证它指向的内存一定是有效的,在使用前使用函数lock()检查weak_ptr是否为空指针。

weak_ptr的操作

操作功能说明
weak_ptr w空weak_ptr,可以指向类型为T的对象。
weak_ptr w(sp)与shared_ptr sp指向相同对象的weak_ptr。T必须能转换为sp指向的类型。
w = pp可以是一个shared_ptr或一个waek_ptr。赋值后w与p共享对象。
w.reset()将w置空
w.use_count()与w共享对象的shared_ptr的数量
w.expired()若w.use_count()为0,返回true,否则返回false。
w.lock()如果expired为true,返回一个空shared_ptr;否则返回一个指向w的对象的shared_ptr。

auto_prt

主要是为了解决“有异常抛出时发生内存泄露”的问题,发生异常而无法正常释放内存。
auto_ptr有拷贝语义,拷贝后源对象变得无效,这可能引发很严重的问题;而unique_ptr无拷贝语义,但提供了移动语义,这样的错误不再发生。
auto_ptr不支持拷贝和赋值操作,不能用在STL标准容器中,因为STL容器中的元素经常要支撑拷贝、赋值操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值