(C++) 手写智能指针(unique_ptr;share_ptr)
创始人
2025-05-31 11:51:23
0

文章目录

  • 前言
  • unique_ptr
    • 描述
    • MyUniquePtr.hpp
    • MyUniquePtr_test.cpp
  • share_ptr
    • 描述
    • MySharePtr.hpp
    • MySharePtr_test.cpp
  • END

前言

RAII

资源获取即初始化Resource Acquisition Is Initialization),或称 RAII,是一种 C++ 编程技术,它将必须在使用前请求的资源(分配的堆内存、执行线程、打开的套接字、打开的文件、锁定的互斥体、磁盘空间、数据库连接等——任何存在受限供给中的事物)的生命周期绑定与一个对象的相绑定。

相较于传统指针的使用不方便(忘记释放内存),C++中提出了智能指针的概念。

从C++98起就有了auto_ptr,而C++11更是弥补了auto_ptr的缺陷,提出了unique_ptr,share_ptr,weak_ptr

注意智能指针的本质就是一个模板类,通过类的内部的操作,来管理资源。

本文简单实现了一下unique_ptr,share_ptr的基础功能;

不考虑数组版特化,不考虑多线程,不考虑删除器,不考虑非成员函数等等

unique_ptr

描述

std::unique_ptr - cppreference.com

unique_ptr 用于维护单个对象,因此不可以拷贝。

但是为了函数传参,反参等操作,允许移动语义。

注意:std::unique_ptr 在类的成员中使用时,该类必须显示在类外写析构。

MyUniquePtr.hpp

/*** std::unique_ptr* https://zh.cppreference.com/w/cpp/memory/unique_ptr*/
#include 
#include template 
class MyUniquePtr {
private:T *m_p = nullptr;public:// 普通构造MyUniquePtr(T *p = nullptr) : m_p(p) {}// 析构~MyUniquePtr() {if (nullptr != this->m_p) {delete this->m_p;this->m_p = nullptr;}}// 移动构造MyUniquePtr(MyUniquePtr &&obj) : MyUniquePtr() {*this = ::std::move(obj);}// 移动赋值MyUniquePtr &operator=(MyUniquePtr &&rhs) {if (this == &rhs) {return *this;}// 夺取右值的资源this->~MyUniquePtr();this->m_p = rhs.m_p;rhs.m_p = nullptr;return *this;}public:  // 修改器// 返回一个指向被管理对象的指针,并释放所有权T *release() {T *res = this->m_p;this->m_p = nullptr;return res;}// 替换被管理对象void reset(T *p = nullptr) {// 直接调用operator=*this = MyUniquePtr(p);}// 交换被管理对象void swap(MyUniquePtr &other) {::std::swap(this->m_p, other.m_p);}public:  // 观察器// 返回指向被管理对象的指针T *get() {return this->m_p;}// 返回用于析构被管理对象的删除器// 本简单样例不考虑删除器// D& get_deleter();// 检查是否有关联的被管理对象operator bool() {return nullptr != get();}// operator*T &operator*() {assert(get());return *this->m_p;}// operator->T *operator->() {assert(get());return this->m_p;}
};

MyUniquePtr_test.cpp

#include 
#include 
#include #include "MyUniquePtr.hpp"struct B {virtual void bar() {std::cout << "B::bar\n";}virtual ~B() = default;
};
struct D : B {D() {std::cout << "D::D\n";}~D() {std::cout << "D::~D\n";}void bar() override {std::cout << "D::bar\n";}
};// 消费 unique_ptr 的函数能以值或以右值引用接收它
MyUniquePtr pass_through(MyUniquePtr p) {p->bar();return p;
}int main() {std::cout << "unique ownership semantics demo\n";{// p 是占有 D 的 unique_ptrMyUniquePtr p(new D());auto q = pass_through(std::move(p));// 现在 p 不占有任何内容并保有空指针assert(!p);// 而 q 占有 D 对象q->bar();}  // ~D 调用于此std::cout << "Runtime polymorphism demo\n";{// p 是占有 D 的 unique_ptr// 作为指向基类的指针MyUniquePtr p(new D());// 虚派发p->bar();// unique_ptr 能存储于容器std::vector> v;v.push_back(new D());v.push_back(std::move(p));v.emplace_back(new D);// 虚派发for (auto& p : v) {p->bar();}}  // ~D called 3 timesreturn 0;
}

share_ptr

描述

std::shared_ptr - cppreference.com

share_ptr 基于计数原理,和传统指针使用起来非常类似。

注意:std::share_ptr 即使基类写虚析构,也会调用多态的析构,而这里的手写没有实现

MySharePtr.hpp

/*** std::shared_ptr* https://zh.cppreference.com/w/cpp/memory/shared_ptr*/
#include 
#include template 
class MySharePtr {
private:T *m_p = nullptr;size_t * m_count = nullptr;public:// 构造MySharePtr(T *ptr = nullptr) : m_p(ptr), m_count(new size_t(0)) {*this->m_count = !!(nullptr != m_p);}// 析构~MySharePtr() {// 保证空指针在调用use_count()也能返回0if (nullptr == this->m_p) {return;}(*this->m_count) -= !!(this->m_p);// 保证m_count有实例if (0 == *(this->m_count)) {if (nullptr != this->m_p) {delete this->m_p;this->m_p = nullptr;}if (nullptr != this->m_count) {delete this->m_count;this->m_count = nullptr;}}}// 拷贝构造MySharePtr(const MySharePtr &obj) : MySharePtr() {*this = obj;}// 拷贝赋值MySharePtr &operator=(const MySharePtr &obj) {if (this == &obj) {return *this;}this->~MySharePtr();this->m_p = obj.m_p;this->m_count = obj.m_count;// 拷贝计数(*this->m_count) += 1;return *this;}// 移动构造MySharePtr(MySharePtr &&obj) : MySharePtr() {*this = ::std::move(obj);}// 移动赋值MySharePtr &operator=(MySharePtr &&obj) {if (this == &obj) {return *this;}this->~MySharePtr();this->m_p = obj.m_p;this->m_count = obj.m_count;// 移动抢占资源,计数不变obj.m_p = nullptr;return *this;}public:  // 修改器// 替换所管理的对象void reset(T *p = nullptr) {*this = p;}// 交换所管理的对象void swap(MySharePtr &other) {using ::std::swap;swap(this->m_p, other.m_p);swap(this->m_count, other.m_count);}public:// 返回存储的指针T *get() {return this->m_p;}// 解引用存储的指针T &operator*() {assert(this->get());return *this->m_p;}// 解引用存储的指针T *operator->() {assert(this->get());return this->m_p;}// 返回 shared_ptr 所指对象的引用计数size_t use_count() {return *this->m_count;}// 检查所管理对象是否仅由当前 shared_ptr 的实例管理bool unique() {return this->use_count() == 1;}// 检查是否有关联的管理对象operator bool() {return this->get() != nullptr;}// 提供基于拥有者的共享指针排序// 本简单样例不考虑删除器// bool owner_before(auto &&other);
};

MySharePtr_test.cpp

#include #include "MySharePtr.hpp"struct Base {Base() {std::cout << "Base::Base()\n";}// ::std::share_ptr 即使不是虚析构也能调用virtual ~Base() {std::cout << "Base::~Base()\n";}
};struct Derived : public Base {Derived() {std::cout << "Derived::Derived()\n";}~Derived() {std::cout << "Derived::~Derived()\n";}
};void fun(MySharePtr p) {MySharePtr lp = p;std::cout << "local pointer in a fun:\n"<< "  lp.get() = " << lp.get()<< ", lp.use_count() = " << lp.use_count() << '\n';
}int main() {MySharePtr p(new Derived);std::cout << "Created a shared Derived (as a pointer to Base)\n"<< "  p.get() = " << p.get()<< ", p.use_count() = " << p.use_count() << '\n';auto tmp = p;p.reset();std::cout << "Shared ownership released\n"<< "ownership from main:\n"<< "  p.get() = " << p.get()<< ", p.use_count() = " << p.use_count() << '\n';fun(tmp);std::cout << "main end\n";
}



END

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
ChatGPT 怎么用最新详细... ChatGPT 以其强大的信息整合和对话能力惊艳了全球,在自然语言处理上面表现出了惊人...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...