【C++ 深入浅出】智能指针shared_ptr、unique_ptr、weak_ptr详解

Mathilda ·
更新时间:2024-11-14
· 618 次阅读

xx智能指针:防止用户忘记释放掉指针所指的堆空间而造成内存泄漏

当一个对象应该被释放时,指向它的智能指针可以确保自动地释放它

智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。C++ 11中最常用的智能指针类型为shared_ptr,它采用引用计数的方法,记录当前内存资源被多少个智能指针引用。该引用计数的内存在堆上分配。当新增一个时引用计数加1,当过期时引用计数减一。只有引用计数为0时,智能指针才会自动释放引用的内存资源。对shared_ptr进行初始化时不能将一个普通指针直接赋值给智能指针,因为一个是指针,一个是类。可以通过make_shared函数或者通过构造函数传入普通指针。并可以通过get函数获得普通指针。

手动释放空间new和delete 类名 *指针名 = new 类名(参数) ... delete 指针

析构函数被执行才能说明内存释放成功

具体实例1.1

#include using namespace std; class Human { public: Human(string name_ = "martin", int age_ = 30) :name(name_), age(age_) { } void greet() { cout << "Hi, how are you? I am " << name << "." << endl; } // 析构函数,如果不定义,系统会默认有一个构造函数 ~Human() { cout << name << " is gone!" << endl; } private: string name; int age; }; int main() { // 情况1:不使用指针 A在栈空间 Human A("meteorsh", 20); A.greet(); // 情况2:使用指针 p在栈空间,但对象Human("daniel", 18)在堆空间 Human* p = new Human("daniel", 18); (*p).greet(); return 0; }

输出:

Hi, how are you? I am meteorsh. Hi, how are you? I am daniel. meteorsh is gone!

解释
从输出可以看到程序会自动释放栈空间的内容,对象A和指针p都被释放了,但是指针p所指的对象Human(“daniel”, 18)在堆空间中,因为我们未使用delete释放这部分内存,所以会造成内存泄漏

题外话
delete的本质:析构函数+C语言的free()函数,作用是销毁对象,并释放与之关联的内存

如果在情况2最后加上delete p

// 情况2:使用指针 p在栈空间,但临时对象Human("daniel", 18)在堆空间 Human* p = new Human("daniel", 18); (*p).greet(); delete p;

则输出结果为

Hi, how are you? I am meteorsh. Hi, how are you? I am daniel. daniel is gone! meteorsh is gone!

注意:此时Human(“daniel”, 18)比A先被析构

动态指针

动态对象的正确释放被证明是编程中极其容易出错的地方,所以为了更加安全地使用动态对象,C++11引入了几个智能指针

auto_ptr (已被C++摒弃) unique_ptr (独占所指对象) shared_ptr (允许多个指针指向同一个对象) weak_ptr (一种弱引用)

上述指针的头文件都是

智能指针(将普通指针封装成一个栈对象)

当一个对象应该被释放时,指向它的智能指针可以确保自动地释放它

类似vector等模版,智能指针也是模版(类模版) C++模版介绍

shared_ptr p1; // 类似 swap (int a, int b) // 或 vector vec;

默认初始化的智能指针中保留着一个空指针

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

使用make_shared是个函数模版,使用时要指定类型 make_shared用其参数来构造给定类型的对象,所以必须有一个构造函数与之匹配
shared_ptr p3 = make_shared(3)

紧接着上面具体实例1.1

... // 情况3:智能指针(将普通指针封装成一个栈对象) shared_ptr p3 = make_shared("kevin", 18); auto p4 = make_shared (2); (*p3).greet();

输出

... Hi, how are you? I am kevin. kevin is gone!

下面的用法是不对的,因为new返回的是一个普通指针
shared_ptr p3 = new Human("kevin", 18); Wrong

但是可以这样初始化智能指针
shared_ptr p3(new Human("kevin", 18));

shared_ptr自动销毁所管理的对象

当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁(它是通过析构函数完成销毁工作的)

shared_ptr的析构函数会递减它所指向的对象的引用计数,如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放它所占用的空间
——参考C++ primer P453


作者:meteorsh



weak c+ UNIQUE 深入浅出 C++

需要 登录 后方可回复, 如果你还没有账号请 注册新账号