[C++] 智能指针(shared_ptr、unique_ptr)

简介: [C++] 智能指针(shared_ptr、unique_ptr)

📢前言

      智能指针是行为类似于指针的类对象,单这种对象还有其他功能。本文介绍三个可帮助管理动态内存分配的智能指针类。先来看看需要哪些功能以及这些功能是如何实现的。请看下面的函数:

void remodel(std::string & str)
{
    std::string * ps = new std::string(str);
    ...
    str = ps;
    return;
}

      您可能发现了其中的缺陷。每当调用时,该函数都分配堆中的内存,单从不回收,从而导致内存泄漏。您可能也知道解决之道——只要别忘了在return语句前添加下面的语句,以释放分配的内存即可:

delete ps;

      然而,但凡涉及“别忘了”的解决方法,很少是最佳的。因为您有时可能忘了,有时可能记住了,但可能在不经意间深处或注释掉了这些代码。即使确实没有忘记,也可能有问题。请看下面的变体:

void remodel(std::string & str)
{
    std::string * ps = new std::string(str);
    ...
    if (weird_thing())
        throw exception();
    str = *ps;
    delete ps;
    return;
}

      当出现异常时,delete将不被执行,因此也将导致内存泄漏。

      如果ps有一个析构函数,该析构函数将在ps过期时释放它指向的内存,那么问题就解决了。因此,ps的问题在于,它只是一个常规指针,不是有析构函数的类对象。如果它是对象,则可以在对象过期时,让它的析构函数删除指向的内存。这正式auto_ptr、unique_ptr和shared_ptr背后的思想。模板shared_ptr允许多个指针指向同一个对象unique_ptr独占一个对象。定义在meory头文件中模板auto_ptr是C++98提供的解决方案,C++11已将其摒弃。然而,虽然auto_ptr被摒弃,单它已经使用了多年;同时,如果您的编译器不支持其他两中解决方法,auto_ptr将是唯一的选择方案。

总结:为什么使用智能指针?

      new申请动态内存,delete释放动态内存,注意有new必须要有相应的delete删除内存,不然会产生内存泄漏问题。为了解决这种内存泄漏问题,标准库包含两种智能指针(unique_ptr和shared_ptr),其可以自动释放内存。


✨动态内存与智能指针

      这三个只能指针模板(auto_ptr/unique_ptr和shared_ptr)都定义了类似指针的对象,可以将new或make_shared获得(直接或间接)的地址赋给这种对像。当只能指针过期时,其析构函数将使用delete来释放内存。因此,如果将new或make_shared返回的地址赋给这些对像,将无需记住稍候释放这些内存:在智能指针过期时,这些内存将自动被释放。

💫智能指针—shared_ptr

定义shared_ptr,定义和其它的模板类似,需要使用<>,默认初始化时会产生空指针。

shared_ptr<int>p1;        //p1为指针,指向string类型
shared_ptr<vector<int>>p2;//p2为指针,指向int的vector

一、分配和使用动态内存的方法:

1)使用new返回地址

std::shared_ptr<Test> p(new Test);

      很显然这段代码会引起一个内存分配,但实际上是有两次内存分配。条款19解释了每个std::shared_ptr会指向一个控制块,这个控制块除了其他一些东西,包含了所指对象的引用计数。控制块的内存分配是在std::shared_ptr的构造函数中进行的。这样直接用new需要为Widget来分配一次内存,还要为控制块再分配一次内存。

2)使用std::make_shared来代替new返回地址

std::shared_ptr<Test> p = std::make_shared<Test>();

      这样一次内存分配就足够了。那是因为std::make_shared会分配一块独立的内存既保存Widget对象又保存控制块。 这个优化减小了程序的静态尺寸,因为代码只包含一次内存分配的调用,同时增加了代码执行速度,因为只有一次内存分配。

总结:make_shared要优于使用new,make_shared可以一次将需要内存分配好,返回一个shared_ptr指针。在memory中定义,是最安全的分配和使用动态内存的方法。

注:要创建智能指针对象,必须包含头文件memory,该文件模板定义。然后使用通常的模板语法来实例化所需类型的指针。

此外,智能指针有一个get函数,可以得到shared_ptr指向对象的内置指针

std::shared_ptr<int>p(new int(2));
int *q=p.get();//q为一个内置指针,指向和p同一个对象,注意不要delete q

💥智能指针—unique_ptr

和shared_ptr不同,unique_ptr拥有它所有的唯一对象,这样就保证unique_ptr被销毁时,其对象也会被销毁。

只能使用new进行直接初始化,不支持普通拷贝和赋值。

std::unique_ptr<int>p1(std::make_shared<int>(2));//直接初始化
std::unique_ptr<int>p2;
p2=p1;//错误,不能同时指向一个对象

但可以所有权转移,使用reset或release。

unique_ptr<int>p1(new int(2));
unique_ptr<int>p2(p1.release());//release会将p1置为空,就是切断p1和它对象的联系
unique_ptr<int>p3(new int(1));
p2.reset(p3.release());//将p3置空并转权给p2;

 


戳戳小手帮忙点个免费的赞和关注吧,嘿嘿。

其他相关描述:C++智能指针shared_ptr、unique_ptr以及weak_ptr

目录
相关文章
|
8天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
27 4
|
24天前
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
1月前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
38 1
|
1月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
29 2
|
1月前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
7天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
33 4
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4
|
1月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
23 4
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)