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

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

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

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

手动释放空间new和delete

类名 *指针名 = new 类名(参数)
...
delete 指针

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

具体实例1.1

#include <iostream>

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 (一种弱引用)

上述指针的头文件都是<memory>

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

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

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

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

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

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

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

紧接着上面具体实例1.1

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

    (*p3).greet(); 

输出

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

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

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

shared_ptr自动销毁所管理的对象

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

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

发布了250 篇原创文章 · 获赞 89 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_43827595/article/details/104384711