手写智能指针unique_ptr、shared_ptr
shared_ptr
shared_ptr主要是维护裸指针和计数器,除了基本的构造函数和析构函数外,还要重载:赋值、拷贝、解引用。
#include<iostream>
template <typename T>
class shared_ptr{
private:
T* ptr;
size_t* cnt;
/*
将引用计数减1,如果减后变为0
说明没有其他实例引用该对象,于是释放占用的内存。
*/
void realse(){
if(cnt && --(*cnt) == 0){
delete ptr;
delete cnt;
}
}
public:
// 初始化
explicit shared_ptr(T* _ptr = nullptr):ptr(_ptr),
cnt(_ptr ? new size_t(1) : nullptr){
}
// 拷贝操作
// 传入other,令this等于other并修改引用计数
shared_ptr(const shared_ptr& other):ptr(other.ptr),cnt(other.cnt){
if(cnt){
++(*cnt);
}
}
//赋值操作
//shared_ptr& 表示返回的是一个引用,除了减少开销外
//还可以支持a = b = c这样的操作
shared_ptr& operator=(const shared_ptr& other){
/*
在赋值运算符重载函数中,通常是比较对象的地址是否相同
常见的做法是将 other 的地址与 this 进行比较,即 this != &other
如果写的是 *this != other,要重载 == 运算符
*/
if( this != &other){
realse();
ptr = other.ptr;
cnt = other.cnt;
if(cnt){
++(*cnt);
}
}
//这里this前加*号是this是指向当前实例的指针
//解引用后才能返回实例(引用)
return *this;
}
//析构函数
~shared_ptr(){
realse();
}
// 有时可能需要对指针进行算数运算,或其它需求
// 总之get()用来支持返回原指针
T* get() const {
return ptr;
}
// 常规的解引用
T* operator->() const {
return *ptr;
}
T& operator*() const {
return *ptr;
}
size_t getCnt() const {
return cnt ? *cnt : 0;
}
};
class Myclass{
public:
Myclass(){
}
void doSomething(){
}
};
int main(){
shared_ptr<Myclass> ptrA(new Myclass());
std::cout<< ptrA.getCnt() << '\n';
shared_ptr<Myclass> ptrB;
ptrB = ptrA;
std::cout<< ptrA.getCnt() << '\n';
std::cout<< ptrB.getCnt() << '\n';
}
unique_ptr
因为是unique,为了保证独占语义,要把拷贝赋值和拷贝构造ban了,对应代码里这两句:
unique_ptr(const unique_ptr &) = delete;
unique_ptr &operator=(const unique_ptr &) = delete;
其它同理,用右值引用实现移动构造和移动赋值。
#include <iostream>
template<typename T>
class unique_ptr{
private:
T* ptr;
public:
explicit unique_ptr(T* _ptr = nullptr):ptr(_ptr){
}
// 移动构造函数:
// 使用右值引用实现移动赋值:
// 先把p.ptr 赋值给新的ptr
// 然后将p.ptr 置空
unique_ptr(unique_ptr &&p) noexcept : ptr(p.ptr){
p.ptr = nullptr;
}
//将拷贝构造运算ban掉,不允许拷贝,符合独占的定义
//其中(const class &)是拷贝构造的常用写法
unique_ptr(const unique_ptr &) = delete;
//同理将拷贝赋值运算ban掉
unique_ptr &operator=(const unique_ptr &) = delete;
//析构函数
~unique_ptr(){
if(ptr)
delete ptr;
}
//移动赋值运算符
inline unique_ptr<T> &operator=(unique_ptr &&p) noexcept{
std::swap(*this,p);
//p是一个右值引用,右值引用是临时的
//出了这个作用域就会被释放掉,满足"移动赋值"的定义
return *this;
}
// 有时可能需要对指针进行算数运算,或其它需求
// 总之get()用来支持返回裸指针
T* get() const {
return ptr;
}
//inline表示内联函数,可以展开函数体直接嵌入
//但编译器不一定采用,只是个建议
//这里重载了解引用运算符(*),且返回的是引用(&)
inline T &operator*() const{
return *get();
}
//重载*运算符,返回原指针
inline T *operator->() const{
return get();
}
//将ptr的值修改为q
inline void reset(T *q = nullptr)noexcept{
if(q != ptr){
if(ptr) delete ptr;
ptr = q;
}
}
//释放资源并返回裸指针
inline T *release(T *q = nullptr) noexcept{
T *res = ptr;
ptr = nullptr;
return res;
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了