简介

在现代 C++ 编程中,标准库包含了智能指针(Smart pointers)。

智能指针用来确保程序不会出现内存和资源的泄漏,并且是"异常安全"(exception-safe)的。

智能指针的使用

智能指针定义在头文件 memory 里的命名空间 std 中。它对于资源获取即初始化(RAII, Resource Acquisition Is Initialization) 编程理念至关重要。该理念的目的是保证对象初始化的时候也是资源获取的时候,从而使对象的所有资源在单行代码中创建。

实践中,RAII 的主要原则就是把任何在堆上分配的资源(比如动态分配的内存或者系统对象的处理)的所有权提供给在栈上分配的对象(其析构函数包含释放资源及相关清理的代码)。

大多数时候,当你初始化一个原始指针或者资源句柄使其指向实际的资源时,立即将其传给智能指针。

在现代 C++ 中,原始指针只用于包含在局部作用域,循环或者工具函数的小块代码中(对性能有要求,并且对资源的所有权也不容易混淆)。

原始指针和智能指针的声明比较如下:

void UseRawPointer()
{
// Using a raw pointer -- not recommended.
Song* pSong = new Song(L"Nothing on You", L"Bruno Mars"); // Use pSong... // Don't forget to delete!
delete pSong;
} void UseSmartPointer()
{
// Declare a smart pointer on stack and pass it the raw pointer.
unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars")); // Use song2...
wstring s = song2->duration_;
//... } // song2 is deleted automatically here.

如上所示,智能指针是一个在栈上声明的类模板,并由指向分配在堆上的对象的原始指针初始化。当智能指针初始化后,它就拥有了原始指针的所有权。这意味着智能指针需要负责原始指针指向的内存释放。智能指针的析构函数包含了 delete 的调用,并且由于智能指针是在栈上声明的,其析构函数会在智能指针对象离开作用域时被调用,即使在栈中发生了异常。

通过使用指针运算符(->*)访问被封装的指针,智能指针类重载了这些运算符以返回被封装的原始指针。

C++ 智能指针的理念类似于在 C# 语言中创建对象的过程:创建对象后让系统负责在正确的时间将其删除。不同之处在于,没有独立的垃圾回收器运行于后台;内存是按照标准 C++ 规范对内存进行管理的,使运行时环境更加快速和高效。

[!重要]

总是在单独的行上创建智能指针,而不是在参数列表中,从而避免由于特定的参数列表分配规则出现一些轻微的内存泄漏

以下示例显示了 C++ 标准库中的 unique_ptr 是如何封装指向大型对象的指针的。

class LargeObject
{
public:
void DoSomething(){}
}; void ProcessLargeObject(const LargeObject& lo){} void SmartPointerDemo()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject()); //Call a method on the object
pLarge->DoSomething(); // Pass a reference to a method.
ProcessLargeObject(*pLarge); } //pLarge is deleted automatically when function block goes out of scope.

上述示例演示了使用智能指针的关键步骤:

  1. 将智能指针声明为局部变量(不要在智能指针上使用 new 或者 malloc 表达式)。
  2. 在类型参数上,指定被封装指针指向的对象类型。
  3. 将指向由 new 创建的对象的指针传给智能指针的构造函数。
  4. 使用重载的操作符 ->* 来访问对象。
  5. 让智能指针来 delete 对象。

智能指针在设计上兼顾了内存和性能的高效性。例如,unique_ptr 唯一的数据成员是被封装的原始指针,这意味着 unique_ptr 具有原始指针同样地大小,4 字节或者 8 字节。通过智能指针重载的操作符 ->* 来访问并不比直接使用原始指针来访问慢多少。

智能指针有其自己的成员函数,通过 . 来访问。例如,一些 C++ 标准库的智能指针有用于重置的成员函数来释放对原始指针的所有权。这可以用于在智能指针超出作用域前释放智能指针管理的内存,看下面的示例:

void SmartPointerDemo2()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject()); //Call a method on the object
pLarge->DoSomething(); // Free the memory before we exit function block.
pLarge.reset(); // Do some other work... }

智能指针通常提供了获取原始指针的方式。 C++ 标准库中的智能指针包含了成员函数 get 来获取原始指针。 CComPtr 有公共的类成员 p。通过获取原始指针,你能够使用智能指针来管理你自己代码涉及的内存并依然能够将原始指针传递给不支持智能指针的代码。

void SmartPointerDemo4()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject()); //Call a method on the object
pLarge->DoSomething(); // Pass raw pointer to a legacy API
LegacyLargeObjectFunction(pLarge.get());
}

智能指针的种类

以下部分总结了在 Windows 环境下不同种类的智能指针,以及如何使用它们。

C++ 标准库中的智能指针

优先使用下列智能指针来封装原始指针指向的纯旧对象(plain old C++ objects,POCO):

  • unique_ptr

    • 对封装的原始指针是独占的
    • 默认用于 POCO,除非你明确的知道你需要一个 shared_ptr
    • 可以移入新的所有者,但不能拷贝或者共享
    • 替代 auto_ptrauto_ptr 已作废
    • 对比 boost::scoped_ptrunique_ptr 更加小巧和高效
    • 长度为一个指针的大小,并且支持右值引用来快速执行 C++ 标准库容器的插入和遍历操作
  • shared_ptr

    • 引用计数智能指针
    • 当你需要将原始指针分派给多个所有者时使用,例如,当你从容器返回一个指针的拷贝并且想要保留它
    • 原始指针不会被 delete 直到所有的 shared_ptr 超出作用域或者放弃所有权。
    • 长度为两个指针的大小,一个用于对象,另一个用于包含引用计数的共享控制块
  • weak_ptr

    • 结合 shared_ptr 使用的特殊智能指针。
    • weak_ptr 提供了对被一个或者多个 shared_ptr 所拥有的对象的访问,但不参与引用计数。
    • 如果你想要监测某个对象,不要求其不被释放,可以使用 weak_ptr
    • 在某些情况下,用于解决 shared_ptr 实例间的循环引用。

扩展

  • 用于 COM 组件的智能指针
  • 用于 POCO对象的ATL智能指针

引用

C++ 中的智能指针-基础的更多相关文章

  1. OSG中的智能指针

    在OpenSceneGraph中,智能指针(Smart pointer)的概念指的是一种类的模板,它针对某一特定类型的对象(即Referenced类及其派生类)构建,提供了自己的管理模式,以避免因为用 ...

  2. RPCZ中的智能指针单例

    RPCZ中的智能指针单例 (金庆的专栏) 智能指针单例应用于 RPCZ 库以实现库的自动初始化与自动清理. RPCZ: RPC implementation for Protocol Buffers ...

  3. Boost中的智能指针(转)

    这篇文章主要介绍 boost中的智能指针的使用.(转自:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html) 内存管理是一 ...

  4. C++中的智能指针、轻量级指针、强弱指针学习笔记

    一.智能指针学习总结 1.一个非const引用无法指向一个临时变量,但是const引用是可以的! 2.C++中的delete和C中的free()类似,delete NULL不会报"doubl ...

  5. ATL和vc++中的智能指针(分别是CComPtr和_com_ptr_t)

    一.智能指针的概念 智能指针是一个类,不是指针,智能指针在所包含的指针不再被使用时候会自动释放该所包含指针所占用的系统资源,而不用手动释放. 原理:智能指针封装了包含指针的AddRef()函数和Rel ...

  6. 标准库中的智能指针shared_ptr

    智能指针的出现是为了能够更加方便的解决动态内存的管理问题.注:曾经记得有本书上说可以通过vector来实现动态分配的内存的自动管理,但是经过试验,在gcc4.8.5下是不行的.这个是容易理解的,vec ...

  7. 智能指针类模板(中)——Qt中的智能指针

    Qt中的智能指针-QPointer .当其指向的对象被销毁时,它会被自动置空 .析构时不会自动销毁所指向的对象-QSharedPointer .引用计数型智能指针 .可以被自由的拷贝和赋值 .当引用计 ...

  8. 智能指针类模板(上)——STL中的智能指针

    智能指针类模板智能指针本质上就是一个对象,它可以像原生指针那样来使用. 智能指针的意义-现代C++开发库中最重要的类模板之一-C++中自动内存管理的主要手段-能够在很大程度上避开内存相关的问题 1.内 ...

  9. C++中的智能指针类模板

    1,智能指针本质上是一个对象,这个对象可以像原生的指针一样使用,因为智能指 针相关的类通过重载的技术将指针相关的操作符都进行了重载,所以智能指针对象可以像原生指针一样操作,今天学习智能指针类模板,通过 ...

随机推荐

  1. Excel常见后缀名

    1.格式.xlsx:excel2007-2016版默认的文件格式,不能有宏: 2.格式.xls:excel97-2003版,可以有宏: 3.格式.csv:以逗号分隔的文本文件,便于兼容其他程序,只保存 ...

  2. 笔趣看小说Python3爬虫抓取

    笔趣看小说Python3爬虫抓取 获取HTML信息 解析HTML信息 整合代码 获取HTML信息 # -*- coding:UTF-8 -*- import requests if __name__ ...

  3. pugixml应用随笔

    1.   修改元素值 second_node.set_value("miller");不对 必须second_node.first_child().set_value(" ...

  4. 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精 确的浮点数运算,包括加减乘除和四舍五入。

    package com.minxinloan.utils; import java.math.BigDecimal; public class Arith { // 源文件Arith.java: /* ...

  5. checkbox限制选中个数

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  6. 开发环境管理利器Vagrant

    引言 不知道你是否经历过,开发环境与生产环境不一致.Windows开发和Linux上的包效果不一样.在我这运行时好的啊 等等等问题,那有没有解决方法呢? 答案就是Vagrant.Docker 1.简介 ...

  7. Linux 防火墙相关操作

    目录 1.查看防火墙状态 2.部署防火墙 3.常用操作 4.其他操作 1.查看防火墙状态 systemctl status firewalld 绿字部分 Active:active(running) ...

  8. springBoot之 spring-boot-starter-parent 引入详解

    springBoot中引入 <parent> <groupId>org.springframework.boot</groupId> <artifactId& ...

  9. CF-1328 F. Make k Equal

    F. Make k Equal 题目链接 题意 长度为n的序列,每次可以选择一个最大的数字将其减一或者选择一个最小的数字将其加一,问最少操作多少次可以使得序列中至少存在 k 个一样的数字 分析 官方题 ...

  10. Educational Codeforces Round 91 (Rated for Div. 2) B. Universal Solution

    题目链接:https://codeforces.com/contest/1380/problem/B 题意 你在和一个机器人玩石头剪刀布,给出一个长为 $n$ 的出拳序列,机器人会从某一处开始出拳 $ ...