C++动态内存管理

C++中,动态内存管理是通过一对运算符来完成:new 和 delete。new操作符在内存中为对象分配空间并返回一个指向该对象的指针,delete接收一个动态对象的指针,销毁该对象,并释放与之相关的内存。

手动管理内存看起来只有这两个操作,似乎很轻松,但实际上这是一件非常繁琐的事情,分配了内存但没有释放内存的场景发生的概率太大了!回想一下,你有多少次打开抽屉却没关上,拿出来的护肤品擦完脸之后却忘了放回去,吃完饭却忘了洗碗。类似这种没有收尾的事情我做的太多了。(以上这些都是在实际生活中我爱人批评我的点)

我连这种明面上的事情都能忘记收尾,何况分配内存!所以为了世界和平,我放弃了手动管理内存。好在C++引入了两种智能指针:shared_ptr和unique_ptr。这两种智能指针可以自动管理内存。(生活中要是有这种东西能自动帮我把东西放回去该多好!)

接下来就介绍一下这两种智能指针的使用方法,使用shared_ptr和unique_ptr需要引入头文件memory。

shared_ptr

shared_ptr能够自动管理内存,当对象不再使用时,自动释放对象所占内存,使用十分方便。

如何创建

创建一个shared_ptr指针最好的方式是使用make_shared函数,使用方式如下

shared_ptr<int> i = make_shared<int>(42)

为了简化写法,我们通常用auto定义一个对象来保存make_shared结果,让编译器自动推导类型。

auto i = make_shared<int>(42)

make_shared函数里面的参数需要和它所保存对象的某一个构造函数相匹配,否则会编译失败。下面是一个构造一个保存有自定义结构体的智能指针。

struct A {
    A(int b) : b(b){};
    int b;
};
// 参数和构造函数相匹配,如果A结构体没有构造方法,下面的将会失败
auto p = std::make_shared<A>(1);
std::cout << p->b << std::endl;

为什么是make_shared

上面说创建一个shared_ptr最好的方式是使用make_shared函数,为什么这样说?看下面的例子

// 重复释放
{
    int* p = new int(1);
    std::shared_ptr<int> sp1(p); 
    std::shared_ptr<int> sp2(p); 
}
// 出作用域,p会被释放两次,程序会崩溃

如果我们使用直接初始化的方式,稍不注意就有可能出现这种形式。为了避免这种,我们还可以把shared_ptr和new一起使用,避免上述情况,入

std::shared_ptr<int> sp1(new int(1));

但这样代码写的就比较多,不划算,不如用make_shared。

如何使用

shared_ptr的操作方法如下:

// 返回p中保存的指针。这个方法需要小心使用,如果保存的指针被释放了,返回的指针所指向的对象也会被释放。
p.get()

// 解引用p,返回p保存的对象
*p

// 获取p所指向对象的mem成员
p->mem

// 交换p和q的指针
p.swap(q)

// 释放p中存放的对象
p.reset()

// 将p指向q
p.reset(q)

// 返回p共享对象的智能指针的数量,主要用于调试
p.use_count()

unique_ptr

某个时刻只能有一个unique_ptr指向一个给定的对象,当unique_ptr被销毁时候,它所指向的对象也被销毁。

如何使用

unique_ptr没有类似make_shared函数,初始化时候需要采用直接初始化的方式。

std::unique_ptr<int> i;
// 或者
std::unique_ptr<int> i(new int(12))

由于unique_ptr拥有它指向的对象,因此unique_ptr不支持赋值和拷贝。不过我们可以使用reset转移unique_ptr的所有权。看下面的例子:

std::unique_ptr<int> k(new int(12));
std::cout << "k" << k << std::endl;
std::unique_ptr<int> l;
// l = k; // 直接拷贝是编译不过去的!
l.reset(k.release()); // 把所有权从k转移给l
std::cout << "l" << l << std::endl;
// k 和 l指向的地址是一样的
赞赏

微信赞赏支付宝赞赏

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注