cpp教程11-手动实现自己的智能指针shared_ptr和 unique_ptr

背景

智能指针是管理操作内存的重要手段
现在我来实现两个重要的指针,分别是shared_ptr和 unique_ptr, 源代码代码和测试都有,代码中带有注释,因为时间关系,有空再补设计思路和原理吧。

shared_ptr

源代码

//
// Created by zxzx on 2020/10/17.
//

#ifndef ZZZ_SharedPtr_H
#define ZZZ_SharedPtr_H
#include <iostream>
#include <memory>
#include <string>
using namespace std;

template <typename T> //首先,这是一个泛型类
class SharedPtr{
public: // 2个构造函数,一个普通指针的有参,一个拷贝构造函数,重载拷贝运算符,重载->,重载星号运算符。
    SharedPtr(T* p=NULL) {
        m_refCount = new int(1);// 我们假设这个传入的指针一定是刚刚分配的堆内存的值,如果传入一个已有的指针,那么应该报错,因为可能会重复释放
        m_p = p;
    };

    SharedPtr(const SharedPtr& sp){
        m_p = sp.m_p; // 指向同一个内存位置
        m_refCount = sp.m_refCount;
        ++*m_refCount; // 引用数加1,实际指向的值是公用的
    }

    SharedPtr& operator=(SharedPtr& p){//指向新的其他对象
        if(this==&p) return *this; // 注意自己给自己赋值!!!,这里可以优化
        decr(); // 释放原来的资源
        m_p = p.m_p;
        m_refCount = p.m_refCount;
        ++*p.m_refCount; // p是一个对象,不是一个指针,所以不能写成 p->m_refCount
        return *this;
    }

    T* operator->(){ // 获取指针
        if(m_p) return m_p;
        throw runtime_error("null pointer1"); // 这里注意异常抛出!!,关于异常,我们还可以继续优化!!
    };

    T& operator*(){
        if(m_p)
            return *m_p;
        throw runtime_error("null pointer2");
    }

    ~SharedPtr(){
        decr();
        cout << "deconstructor" << endl;
    }

    int getRefCount(){ // 这个必须有!
        return *m_refCount;
    }

private:
    T* m_p;
    int* m_refCount; // 这个指针

    void decr(){
        --*m_refCount;
        if( *m_refCount == 0){
            cout << " free" << endl;
            delete m_p;
            delete m_refCount;
            m_p = NULL;
            m_refCount = NULL;
        }
    }
};


#endif //ZZZ_SharedPtr_H

测试代码

#include <memory>
#include <iostream>
#include "SharedPtr.h"
using namespace std;

class Test{

public:
    Test(){name = "";};

    Test(char* i) {
        name = i;
    }

    void showName(){
        cout << name << endl;
    }

    string& getName(){
        return name;
    }

private:
    string name; // 不是堆上的对象,析构的时候会自动释放的!!
};


void test_ptr(){
    SharedPtr<Test> p1(new Test("abcd"));
    p1->showName();
    SharedPtr<Test> p2(p1);
    *p2 = Test("defg");
    p1->showName();
    cout <<"@1:" << p1.getRefCount() << endl;

    SharedPtr<Test> p3 (new Test("xxx"));
    p3 = p2;
    cout <<"@2:" << p1.getRefCount() << endl;
}

int main(){
    test_ptr();
}

unique_ptr

源代码

//
// Created by zxzx on 2020/10/17.
//

#ifndef ZZZ_SMARTPTR_H
#define ZZZ_SMARTPTR_H
#include <iostream>
#include <memory>
#include <string>
using namespace std;

template <typename T> //首先,这是一个泛型类
class UniquePrt{
public: // 2个构造函数,一个普通指针的有参,一个拷贝构造函数,重载拷贝运算符,重载->,重载星号运算符。
    UniquePrt(T* p= nullptr):m_p(p) {};
    UniquePrt(const UniquePrt& sp) = delete;
    UniquePrt& operator=(UniquePrt& p) = delete; // 改用reset命令,或者移动赋值

    UniquePrt(const UniquePrt&& other):m_p(other.m_p){
        other.m_p = nullptr;
    };

    UniquePrt<T>& operator=(UniquePrt<T>&& other){ // 转移赋值, 之前的是空的,所以不用处理。万一不是空的呢?这个系统会报异常的吗?
        cout << "move assign" << endl;
        swap(other); // other现在保存的是原来的指针,等离开命名空间,就会自动被析构,这块不用担心,这里我们也不用自己再释放了
        return *this; //
    };

    ~UniquePrt(){
        if(m_p) delete m_p;
        cout << "deconstructor" << endl;
    }

    T* operator->() const{ // 箭头重载
        cout << "-> reload" << endl;
        return m_p;
    };

    T& operator*() const{ // 指针解析运算符重载
        return *m_p;
    }

    T* release(){ // 释放指针并返回原来的指针
        T* ret = m_p;
        m_p = nullptr; //
        return ret;
    }

    void reset(T* p = nullptr){ // 释放以前的指针,换一个新的
        T* old = m_p;
        if(p != m_p){
            if(m_p) delete m_p;
        }
        m_p = p;
    }

    void swap(UniquePrt& p){
        using std::swap;
        swap(m_p, p.m_p); // 注意交换指针属性,而不是别的
    }

    explicit operator bool() const noexcept {
        return m_p != nullptr; // 这样就棒的多!!
    }
private:
    T* m_p;
};

#endif //ZZZ_SMARTPTR_H

测试代码

#include <memory>
#include <iostream>
#include "UniquePtr.h"
using namespace std;

class A{

public:
    ~A(){
        cout <<"kill A\n";
    }
};
// 如果程序执行过程中抛出了异常,unique_ptr就会释放它所指向的对象
// 传统的new 则不行
unique_ptr<A> fun1()
{
    unique_ptr<A> p(new A());
    //do something
    return p;
}

void fun2()
{   //  unique_ptr具有移动语义
    unique_ptr<A> p = fun1();// 使用移动构造函数
    // do something
}// 在函数退出的时候,p以及它所指向的对象都被删除释放


class Test{
public:
    Test(){name = "";};
    Test(string& i) {
        name = i;
    }

    Test(string i) {
        name = i;
    }

    Test(char* i) {
        name = i;
    }

    ~Test(){}

    void showName(){
        cout << name << endl;
    }
    string& getName(){
        return name;
    }

private:
    string name;
};


void test_ptr(){
    UniquePrt<Test> p1(new Test("abcd"));
    p1->showName();
//    UniquePrt<Test> p2;
//    p2 = UniquePrt<Test>(new Test("zzz"));
    p1.reset(new Test("124"));
    p1->showName();

    Test* t = p1.release();
    cout << "t:" <<  t->getName() << endl;
    // 这里做了一个很顽皮的实验,如果把 unique_ptr指向的元素取出来,然后获取它的指针,然后让另外一个unique_ptr接管,那么就出问题了
//    p1.reset( &(*p2)); // 报错了pointer being freed was not allocated
//    UniquePrt<Test> p3 (new Test("xxx"));

}

int main(){
    test_ptr();
}

参考

[1]手动实现一个 unique_ptr
https://www.jianshu.com/p/77c2988be336

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值