C++动态内存&智能指针

本文详细介绍了C++中动态内存管理和智能指针(shared_ptr、unique_ptr、weak_ptr)的工作原理、使用方法,包括make_shared函数、拷贝和赋值规则、delete器函数以及它们在处理动态数组和循环引用中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态内存

C++中 动态内存管理 通过 new 和 delete实现

malloc() 函数在 C 语言中就出现了,在 C++ 中仍然存在,但建议尽量不要使用 malloc() 函数。
new 与malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。
使用例子:
double* pvalue = NULL;
pvalue = new double;

智能指针

智能指针:自动释放所指向的对象
分为

  1. shared_ptr
  2. unique_ptr
  3. weak_ptr

shred_ptr

类似vector,智能指针也是模板,需要提供指针指向的类型。
原理:
shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用它一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,释放所指向的堆内存。shared_ptr内部的引用计数是安全的,但是对象的读取需要加锁。

格式为:
shared_ptr name
例子:
shared_ptr name;
默认初始化的智能指针中保存这一个空指针

make_shared函数

依赖头文件 memory
make_shared函数动态申请内存分配一个对象并初始化它,返回shared_ptr指针
使用例子:
shared_ptr p3 = make_shared(42);

shart_ptr 拷贝和赋值

当进行拷贝或赋值操作时,每个shared_ptr都会纪录有多少个其他shared_ptr指向相同的对象
无论何时我们拷贝一个 shared_ptr,计数器都会递增。例如,当用一个 shared_ptr 初始化另一个 shared_ptr,或将它作为参数传递给一个函数以及作为函数的返回值时,它所关联的计数器就会递增。
当我们给 shared_ptr 赋予一个新值或是 shared_ptr 被销毁(例如一个局部的 shared_ptr 离开其作用域)时,计数器就会递减。

auto r = make_shared<int>(42); // r指向的int只有一个引用者
r = q;
// 给r赋值,令它指向另一个地址
// 递增q指向的对象的引用计数
// 递减r原来指向的对象的引用计数
// r原来指向的对象已没有引用者,会自动释放

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

shared_prt类的get函数返回一个内置指针,指向智能指针所管理的对象
此函数的设计情况:我们需要向不能使用智能指针的代码传递一个内置指针
get函数将内存的访问权限传递给一个指针,但是之后代码不会delete该内存的情况下,对get函数的使用才是最安全的。
永远不要用get初始化另一个智能指针或者为另一个智能指针赋值。

正确使用例子:

void process(shared_ptr<int> ptr){ 
	... // 使用ptr
} // ptr 离开作用域,被销毁
shared_ptr<int> p(new int(42)); //初始化一个智能指针对象p,引用计数+1
process(p);  //p所指的对象引用计数=1,引用计数总数为2
//process函数调用之后,p所指的引用计数减1
int i=*p; //正确

删除器函数

当shared_ptr生命周期结束时,会调用默认的析构函数来释放(delete)自己所指向的内存空间。但是我们可以使用shared_prt的语法来指定删除器函数,那么在shared_ptr生命周期结束时就会自动调用这个函数。

使用例子:

void end_connection(connection *p)//删除器函数
{
    disconnection (*p);
}
 
void f(destination &d)
{
    connection c=connect(&d);
    shared_ptr<connection> p(&c,end_connection);
    ....//使用这个连接
    
    //当f函数退出或者异常退出,p都会调用end_connection函数
}

shared_ptr与动态数组的使用

shared_ptr不直接支持管理动态数组。如果希望使用shared_ptr管理动态数组,必须提供自己定义的删除器
如果未提供删除器,shared_ptr默认使用delete删除动态数组,此时delete少一个“[]”,因为会产生错误

//本例中,传递给shared_ptr一个lambda作为删除器
shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; } );
shared_ptr<int> sp2(new int[3]{1,2,3}, [](int *p) { delete[] p; });
sp2.reset();  //使用自己书写的lambda释放数组

动态数组的访问:shared_ptr不支持点和箭头成员运算符访问数组,并且不提供下标运算符访问数组,只能通过get()函数来获取一个内置指针,然后再访问数组元素

unique_ptr

unique_ ptr实现了独享被管理对象指针的概念,这意味这它可确保一个对象和其对应的资源同一时间只被一个unique_ ptr对象拥有。一旦拥有者 被销毁或者变成empty或者开始拥有另一个对象的地址,先前拥有的那个对象就会被销毁,其任何相应资源亦会被释放。

使用方式:

unqiue_ptr<int>a1(new int(10));

无法使用拷贝构造和赋值操作

unique_ptr是没有拷贝构造和赋值函数的,把这两个函数已经删除
正确使用方式:

unqiue_ptr<int>a1(new int(10));

错误使用方式:

unique_ptr<int>a2(a1);
unique_ptr<int>a3;
a3=a1;//err

可以进行移动构造和移动赋值

unique_ptr不能和其他指针共享,只能将资源转移,所以是std::move()可以让它达到目的

int main()
{
   std::unique_ptr<int>a1(new int(10));
   std::unique_ptr<int>a2=std::move(a1);//移动赋值
   std::unique_ptr<int>a3(std::move(a2));//移动构造
   return 0;
}

可以从函数中返回一个unique_ptr

虽然不支持拷贝操作,但可以从一个函数中返回一个唯一性智能指针。

unique_ptr<int>fun(int num)
{
     unique_ptr<int>op(new int(num));
     return op;
     
}
int main()
{
     int s=1;
     unique_ptr<int>a1=fun(s);
     return 0;
}

weak_ptr

share_ptr虽然已经很好用了,但是有一点share_ptr智能指针还是有内存泄露的情况,当两个对象相互使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。这时,就需要配合使用weak_ptr

std::weak_ptr 是一种智能指针,通常不单独使用,只能和 shared_ptr 类型指针搭配使用,可以视为 shared_ptr 指针的一种辅助工具。借助 weak_ptr 类型指针可以获取 shared_ptr 指针的一些状态信息,比如有多少指向相同的 shared_ptr 指针、通过expired()判断shared_ptr 指针指向的堆内存是否已经被释放等等,还可以解决shared_ptr 循环引用的问题。
weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加

weak_ptr有以下特点:

  1. weak_ptr 通常情况下需要跟 shared_ptr 一起使用
  2. 构造和析构时引用计数不增加也不会减少
  3. 专门用来解决两个类中shared_ptr相互引用而导致的内存泄露问题

循环引用代码示例:

#include <iostream>
#include <memory>
using namespace std;
 
class B;    //声明
class A
{
public:
    shared_ptr<B> pb_;
    ~A()
    {
        cout << "A delete\n";
    }
};
 
 
class B
{
public:
    shared_ptr<A> pa_;
    ~B()
    {
        cout << "B delete\n";
    }
};
 
 
void fun()
{
    shared_ptr<B> pb(new B());
    shared_ptr<A> pa(new A());
    cout << pb.use_count() << endl;    //1
    cout << pa.use_count() << endl;    //1
    pb->pa_ = pa;
    cout << pb.use_count() << endl; 
    cout << pa.use_count() << endl;
    pa->pb_ = pb;
    cout << pb.use_count() << endl;    //2
    cout << pa.use_count() << endl;    //2
}
 
 
int main()
{
    fun();
    return 0;
}

输出结果:

1
1
1
2
2
2

可以看到fun函数中pa ,pb之间互相引用,两个资源的引用计数为2,当要跳出函数时,智能指针pa,pb析构时两个资源引用计数会减1,但是两者引用计数还是为1,导致跳出函数时资源没有被释放(A、B的析构函数没有被调用)运行结果没有输出析构函数的内容,造成内存泄露。
这种情况下,把其中一个改为weak_ptr就可以了。
我们把类A里面的shared_ptr pb_,改为weak_ptr pb_:

class A
{
public:
        weak_ptr<B> pb_;
        //shared_ptr<B> pb_;
        ~A()
        {
                cout << "A delete\n";
        }
};

输出结果:

1
1
1
2
1
2
B delete
A delete
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值