(C++) 手写智能指针(unique_ptr;share_ptr)

文章目录

  • 前言
  • 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 <typename T>
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<T>(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<D> pass_through(MyUniquePtr<D> p) {
    p->bar();
    return p;
}

int main() {
    std::cout << "unique ownership semantics demo\n";
    {
        // p 是占有 D 的 unique_ptr
        MyUniquePtr<D> 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<B> p(new D());
        // 虚派发
        p->bar();

        // unique_ptr 能存储于容器
        std::vector<MyUniquePtr<B>> 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 times

    return 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 <typename T>
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()也能返回0
        if (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<Base> p) {
    MySharePtr<Base> lp = p;
    std::cout << "local pointer in a fun:\n"
              << "  lp.get() = " << lp.get()
              << ", lp.use_count() = " << lp.use_count() << '\n';
}

int main() {
    MySharePtr<Base> 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

你可能感兴趣的:(C/C++,c++,开发语言)