Mr Dk.'s BlogMr Dk.'s Blog
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 📝 Notes
    • Algorithm
      • Algorithm - Bloom Filter
      • Algorithm - Disjoint Set
      • Algorithm - Fast Power
      • Algorithm - KMP
      • Algorithm - Monotonic Stack
      • Algorithm - RB-Tree
      • Algorithm - Regular Expression
      • Algorithm - Sliding Window
      • Online Judge - I/O
    • C++
      • C++ - Const
      • C++ File I/O
      • C++ - Object Layout
      • C++ - Operator Overload
      • C++ - Polymorphism
      • C++ STL algorithm
      • C++ STL map
      • C++ STL multimap
      • C++ STL priority_queue
      • C++ STL set
      • C++ STL string
      • C++ STL unordered_map
      • C++ STL vector
      • C++ - Smart Pointer
      • C++ - Template & Genericity
    • Compiler
      • ANTLR - Basic
      • Compiler - LLVM Architecture
      • Compiler - Multi-version GCC
    • Cryptography
      • Cryptography - Certbot
      • Cryptography - Digital Signature & PKCS #7
      • Cryptography - GPG
      • Cryptography - JWT
      • Cryptography - Keystore & Certificates
      • Cryptography - OAuth 2.0
      • Cryptography - Java 实现对称与非对称加密算法
      • Cryptography - TLS
    • DevOps
      • DevOps - Travis CI
    • Docker
      • Docker - Image & Storage Management
      • Docker - Image
      • Docker - Libcontainer
      • Docker - Multi-Arch Image
      • Docker - Multi-Stage Build
      • Docker - Network
      • Docker - Orchestration & Deployment
      • Docker - Overview
      • Docker - Service Building
      • Docker - Volume & Network Usage
      • Docker - Volume
      • Linux - Control Group
      • Linux - Namespace
    • Git
      • Git - Branch & Merge
      • Git - Cached
      • Git - Cherry Pick
      • Git - Commit
      • Git - Patch
      • Git - Proxy
      • Git - Rebase
      • Git - Reset
      • Git - Stash
      • Git - Theme for Git-Bash
    • Java
      • JVM - Synchronized
      • JVM - Volatile
      • Java - Annotation 注解
      • Java - BIO & NIO
      • Java - Class Path
      • Java - Condition and LockSupport
      • Java - Current Timestamp
      • Java - Deep Copy
      • Java - 运行环境配置
      • Java - Equals
      • Java - Exporting JAR
      • Java - Javadoc
      • Java - Lock
      • Java - Maven 项目构建工具
      • Java - References
      • Java - Reflection Mechanism
      • Java - String Split
      • Java - Thread Pool
      • Java - Thread
      • Tomcat - Class Loader
      • Tomcat - Container
    • Linux
      • addr2line
      • cut
      • df
      • du
      • fallocate
      • find
      • fio
      • grep
      • groupadd
      • gzip
      • head / tail
      • hexdump
      • iostat
      • iotop
      • kill
      • ldd
      • lsof
      • ltrace / strace
      • mpstat
      • netstat
      • nm
      • pidstat
      • pmap
      • readlink
      • readlink
      • rpm2cpio / rpm2archive
      • sort
      • tee
      • uniq
      • useradd
      • usermod
      • watch
      • wc
      • which
      • xargs
    • MS Office
      • MS Office - Add-in Dev
      • MS Office - Application
    • MySQL
      • InnoDB - Architecture
      • InnoDB - Backup
      • InnoDB - Checkpoint
      • InnoDB - Critical Features
      • InnoDB - Files
      • InnoDB - Index
      • InnoDB - Insert Buffer
      • InnoDB - Lock
      • InnoDB - Partition Table
      • InnoDB - Table Storage
      • MySQL - Server Configuration
      • MySQL - Storage Engine
    • Network
      • Network - ARP
      • Network - FTP
      • Network - GitHub Accelerating
      • HTTP - Message Format
      • HTTP - POST 提交表单的两种方式
      • Network - Proxy Server
      • Network - SCP
      • Network - SSH
      • Network - TCP Congestion Control
      • Network - TCP Connection Management
      • Network - TCP Flow Control
      • Network - TCP Retransmission
      • Network - Traceroute
      • Network - V2Ray
      • Network - WebSocket
      • Network - Windows 10 Mail APP
      • Network - frp
    • Operating System
      • Linux - Kernel Compilation
      • Linux - Multi-OS
      • Linux - Mutex & Condition
      • Linux - Operations
      • Linux: Package Manager
      • Linux - Process Manipulation
      • Linux - User ID
      • Linux - Execve
      • OS - Compile and Link
      • OS - Dynamic Linking
      • OS - ELF
      • Linux - Image
      • OS - Loading
      • OS - Shared Library Organization
      • OS - Static Linking
      • Syzkaller - Architecture
      • Syzkaller - Description Syntax
      • Syzkaller - Usage
      • Ubuntu - Desktop Recover (Python)
      • WSL: CentOS 8
    • Performance
      • Linux Performance - Perf Event
      • Linux Performance - Perf Record
      • Linux Performance - Perf Report
      • Linux Performance - Flame Graphs
      • Linux Performance - Off CPU Analyze
    • PostgreSQL
      • PostgreSQL - ANALYZE
      • PostgreSQL - Atomics
      • PostgreSQL - CREATE INDEX CONCURRENTLY
      • PostgreSQL - COPY FROM
      • PostgreSQL - COPY TO
      • PostgreSQL - Executor: Append
      • PostgreSQL - Executor: Group
      • PostgreSQL - Executor: Limit
      • PostgreSQL - Executor: Material
      • PostgreSQL - Executor: Nest Loop Join
      • PostgreSQL - Executor: Result
      • PostgreSQL - Executor: Sequential Scan
      • PostgreSQL - Executor: Sort
      • PostgreSQL - Executor: Unique
      • PostgreSQL - FDW Asynchronous Execution
      • PostgreSQL - GUC
      • PostgreSQL - Locking
      • PostgreSQL - LWLock
      • PostgreSQL - Multi Insert
      • PostgreSQL - Plan Hint GUC
      • PostgreSQL - Process Activity
      • PostgreSQL - Query Execution
      • PostgreSQL - Spinlock
      • PostgreSQL - Storage Management
      • PostgreSQL - VFD
      • PostgreSQL - WAL Insert
      • PostgreSQL - WAL Prefetch
    • Productivity
      • LaTeX
      • Venn Diagram
      • VuePress
    • Solidity
      • Solidity - ABI Specification
      • Solidity - Contracts
      • Solidity - Expressions and Control Structures
      • Solidity - Layout and Structure
      • Solidity - Remix IDE
      • Solidity - Slither
      • Solidity - Types
      • Solidity - Units and Globally Available Variables
    • Vue.js
      • Vue.js - Environment Variable
    • Web
      • Web - CORS
      • Web - OpenAPI Specification
    • Wireless
      • Wireless - WEP Cracking by Aircrack-ng
      • Wireless - WPS Cracking by Reaver
      • Wireless - wifiphisher

C++ - Smart Pointer

Created by : Mr Dk.

2021 / 09 / 19 20:00

Ningbo, Zhejiang, China


学习一下 C++ 中四种常见智能指针的用法、行为和具体实现。它们的定义位于:

#include <memory>
using namespace std;

auto_ptr (deprecated in C++ 11)

Automatic Pointer,已经在 C++ 11 标准中过时。

智能指针本身也是一个对象,在内部维护了实际的指针。当 auto_ptr 对象的生命周期结束时,在对象的 析构函数 中实现对指针的销毁 (通常为 operator delete)。

该指针提供了对一个指针的全周期生命管理,语义上类似于 独占 一个指针,维护着对内部指针的 控制权。对指针拥有控制权的 auto_ptr 负责对内部指针进行销毁。因此,不可能有多于两个 auto_ptr 对象在内部维护着同一个指针。当两个 auto_ptr 对象之间发生赋值时,将会涉及到控制权的转移。控制权转移之后,原指针将失效。

另外,可以看到构造函数都带有 throw,保证是 异常安全 的。

explicit auto_ptr (X* p=0) throw();

auto_ptr (auto_ptr& a) throw();
template<class Y>
  auto_ptr (auto_ptr<Y>& a) throw();

auto_ptr (auto_ptr_ref<X> r) throw();
~auto_ptr() throw();

常规指针行为:

X* get() const throw();        // 获取内部指针
X& operator*() const throw();  // 通过 operator* 对内部指针解引用
X* operator->() const throw(); // 通过 operator-> 返回内部指针的成员变量

控制权管理:

// 指针赋值,左边的 auto_ptr 对象接管控制权,右边的 auto_ptr 对象置空
auto_ptr& operator= (auto_ptr& a) throw();
template <class Y>
  auto_ptr& operator= (auto_ptr<Y>& a) throw();
auto_ptr& operator= (auto_ptr_ref<X> r) throw();

// 放弃控制权,将 auto_ptr 内部指针置空,但不销毁指针指向的空间
X* release() throw();

// 放弃控制权,并销毁内部指针指向的空间
// 然后内部指针被重新初始化 (或置空)
void reset (X* p=0) throw();

auto_ptr 使用了 copy 语义来实现转移指针,但其行为并不与 copy 语义一致。因为原对象释放了所有权。因此在 C++ 11 中被 unique_ptr 替代,因为它实现了 move 语义。

unique_ptr

它的语义如其名称所述,表示独占一个资源。一旦它对一个指针资源拥有控制权,就需要在析构函数中负责对这个指针进行销毁。被赋值或主动放弃控制权时也是如此。unique_ptr 在语义上独占了指针,因此在销毁指针时并不考虑这个指针是否被其它对象持有。

其内部元素包含:

  • Pointer:在构造时被赋值,在赋值 / reset 时可被替换,也可以被独立访问
  • Deleter:一个 可调用对象,接收参数为相同指针类型,在构造时被设置,可以通过赋值替换,用于销毁其管理的指针

构造函数:

// 空构造函数,内部指针被设置为 nullptr
constexpr unique_ptr() noexcept;
constexpr unique_ptr (nullptr_t) noexcept : unique_ptr() {}

// 从指针构造
explicit unique_ptr (pointer p) noexcept;
// 从指针构造,并复制一份输入 deleter
unique_ptr (pointer p,
    typename conditional<is_reference<D>::value,D,const D&> del) noexcept;
// 从指针构造,并使用输入 deleter
unique_ptr (pointer p,
    typename remove_reference<D>::type&& del) noexcept;

// move 语义构造:接管指针,复制一份 deleter
unique_ptr (unique_ptr&& x) noexcept;
template <class U, class E>
  unique_ptr (unique_ptr<U,E>&& x) noexcept;
template <class U>
  unique_ptr (auto_ptr<U>&& x) noexcept;

// !!! 拷贝构造函数被禁用,因为指针是被独占的
unique_ptr (const unique_ptr&) = delete;

析构函数:

~unique_ptr();  // 以内部指针为参数,调用 deleter

指针行为:

// 判空,不再需要先通过 get 获取到内部指针,再对内部指针进行判空
explicit operator bool() const noexcept;

// 重载 operator* 和 operator->
typename add_lvalue_reference<element_type>::type operator*() const;
pointer operator->() const noexcept;

控制权管理:

// 放弃指针控制权,内部指针置空
pointer release() noexcept;

// 放弃指针控制权并销毁
// (可选) 接管输入参数中的指针控制权
void reset (pointer p = pointer()) noexcept;

// 与另一个 unique_ptr 对换控制权
void swap (unique_ptr& x) noexcept;

shared_ptr

该对象允许与其它 shared_ptr 对象共享对一个指针的控制权。这一组共享者中最后一个释放控制权的对象负责销毁该指针。共享控制权的唯一方式是复制内部指针,并维护引用计数。

内部的指针:

  • Stored pointer:指向需要被管理的对象
  • Owned pointer:指向控制权对象组用于管理何时销毁 stored pointer 的数据

构造函数:

// 空构造函数,不管理任何指针,引用计数为 0
constexpr shared_ptr() noexcept;
constexpr shared_ptr(nullptr_t) : shared_ptr() {}

// 默认构造函数,可选指针、deleter、allocator
template <class U> explicit shared_ptr (U* p);
template <class U, class D> shared_ptr (U* p, D del);
template <class D> shared_ptr (nullptr_t p, D del);
template <class U, class D, class Alloc> shared_ptr (U* p, D del, Alloc alloc);
template <class D, class Alloc> shared_ptr (nullptr_t p, D del, Alloc alloc);

// 拷贝构造函数
// 如果输入参数不为空,则共享控制权,并增加 ref count
// 如果输入参数为空,那么创建一个空的对象
shared_ptr (const shared_ptr& x) noexcept;
template <class U> shared_ptr (const shared_ptr<U>& x) noexcept;
// 从 weak_ptr 构造:如果输入参数过期,那么抛出 bad_weak_ptr 异常
template <class U> explicit shared_ptr (const weak_ptr<U>& x);

// 移动构造函数
// 构造完毕后,输入参数被置空
shared_ptr (shared_ptr&& x) noexcept;
template <class U> shared_ptr (shared_ptr<U>&& x) noexcept;

// 从 auto_ptr 或 unique_ptr 转移控制权,入参丢失控制权而被置空
template <class U> shared_ptr (auto_ptr<U>&& x);
template <class U, class D> shared_ptr (unique_ptr<U,D>&& x);

// 别名构造函数
// 对象不管理 p 指向的存储,而是共享 x 的数据并累加引用计数
// 通常被用于指向已经被纳入管理的 object member
template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;

析构函数:

  • 如果引用计数大于 1,那么引用计数--
  • 如果引用计数等于 1,那么使用 deleter 或 operator delete 销毁指针
  • 如果引用计数为 0,那么没有任何效果
~shared_ptr();

重置:

// 当前对象被自销毁
void reset() noexcept;
// (可选) 然后对一个新指针建立控制权
template <class U> void reset (U* p);
template <class U, class D> void reset (U* p, D del);
template <class U, class D, class Alloc> void reset (U* p, D del, Alloc alloc);

引用计数相关:

long int use_count() const noexcept;  // 返回引用计数
bool unique() const noexcept;         // 返回控制权是否唯一

其余与 unique_ptr 类似。

weak_ptr

shared_ptr 互相引用时,引用计数永远不会减至 0。weak_ptr 用于解决这个问题。它可以被 shared_ptr 赋值,但不增加引用计数。

构造函数:

// 成为共享组的一部分,但不持有控制权 (不增加引用计数)
constexpr weak_ptr() noexcept;

weak_ptr (const weak_ptr& x) noexcept;
template <class U> weak_ptr (const weak_ptr<U>& x) noexcept;

template <class U> weak_ptr (const shared_ptr<U>& x) noexcept;

析构函数无任何作用。

引用计数相关:

// 返回共享内部指针控制权的指针数量
long int use_count() const noexcept;
// 返回当前 weak_ptr 是否过期 (use_count()==0)
bool expired() const noexcept;
// 返回当前 weak_ptr 对应的 shared_ptr (如果没有过期)
shared_ptr<element_type> lock() const noexcept;

weak_ptr 没有重载 operator* 和 operator->,因此只能通过 shared_ptr 访问堆空间。

没太搞懂它的具体使用场景。


References

cplusplus.com - auto_ptr

cplusplus.com - unique_ptr

cplusplus.com - shared_ptr

cplusplus.com - weak_ptr

Edit this page on GitHub
Prev
C++ STL vector
Next
C++ - Template & Genericity