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 <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;
}
};
#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;
}
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 <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);
};
#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";
}