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的基础功能;
不考虑数组版特化,不考虑多线程,不考虑删除器,不考虑非成员函数等等
std::unique_ptr - cppreference.com
unique_ptr 用于维护单个对象,因此不可以拷贝。
但是为了函数传参,反参等操作,允许移动语义。
注意:
std::unique_ptr在类的成员中使用时,该类必须显示在类外写析构。
/*** 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;}
};
#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;
}
std::shared_ptr - cppreference.com
share_ptr 基于计数原理,和传统指针使用起来非常类似。
注意:
std::share_ptr即使基类写虚析构,也会调用多态的析构,而这里的手写没有实现
/*** 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);
};
#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";
}