【C++11】智能指针 | 重拳出击 -> 实现自己的智能ptr

本文介绍了C++11中的智能指针,包括智能指针的作用和RAII原则。讨论了自定义AutoPtr的缺点,解释了为何在析构时可能出现二次删除问题。接着探讨了unique_ptr的特性,强调了其不可复制性以及移动构造函数和赋值运算符的重要性。最后,概述了shared_ptr和weak_ptr的概念,指出它们在多所有者场景下的用途,并简要提及了内部的引用计数机制。
摘要由CSDN通过智能技术生成

智能指针

参考资料:https://docs.microsoft.com/zh-cn/cpp/cpp/smart-pointers-modern-cpp?view=msvc-160

  1. 本质: 是一个模板类,利用RAII的计数对普通的指针进行封装。
  2. RAII 主要原则: 是为将任何堆分配资源(例如,动态分配内存或系统对象句柄)的所有权提供给其析构函数包含用于删除或释放资源的代码以及任何相关清理代码的堆栈分配对象。
  3. 作用: 用来管理用户的类对象.
  4. 智能指针析构函数:包括要删除的调用,并且由于在堆栈上声明了智能指针,当智能指针超出范围时将调用其析构函数,尽管堆栈上的某处将进一步引发异常。

实现自己的AutoPtr(弃用)

template <typename T>
class AutoPtr {
public:
    explicit AutoPtr (T* ptr = nullptr) {
        std::cout << "set new object, My Class = " << ptr << std::endl;
        _ptr = ptr;
    }

    ~AutoPtr () {
        std::cout << "delete object, My Class = " << _ptr << std::endl;
        if (_ptr != nullptr) {
            delete _ptr;
        }
    }
    
    T* operator-> () {
        // cout << " this =  " << this << endl;
        // cout << " this->_ptr =  " << this->_ptr << endl;
        return this->_ptr;
    }

    T& operator* () {
        // this->ptr指向的是myclass
        return *(this->_ptr);
    }

private:
    T* _ptr;
};
class MyClass {
public:
    MyClass () {
        cout << " construct MyClass" << endl;
    }
    ~MyClass () {
        cout << " delete MyClass" << endl;
    }
    void print() {
        cout << "this = " << this << endl;
        // std::cout << "Hello world ~~ !" << std::endl;
    }
};

首先要明白:使用智能指针管理自己的类,因此,智能指针只是我们的一个工具tool。

int main(int argc, char* argv[]) {
    // 智能指针其实就是用指针去管理你的类;
    AutoPtr<MyClass> myAutoPtr(new MyClass());
    
	// class AutoPtr 已经实现了* 和 ->的重载,为了是让这个myAutoPtr看起来更像一个指针。
    myAutoPtr.operator->()->print();
    myAutoPtr.operator*().print();
    return 0;
}

上述代码的缺点

缺点就是:当两个指针管理同一个类的时候,因为这两个智能autoptr指向同一个对象(内存空间)。
当生命周期结束的时候,两个智能autoptr类会主动析构,析构指向的对象两次,从而出现异常。

主函数:

int main(int argc, char* argv[]) {
    // 智能指针其实就是用指针去管理你的类;
    AutoPtr<MyClass> myAutoPtr(new MyClass());
	AutoPtr<MyClass> myAutoPtr2 = myAutoPtr;
    return 0;
}

在这里插入图片描述
疑问:为什么AutoPtr加了if判空语句,第二次还是会对堆进行二次删除呢,难道_ptr第二次删除的时候不应该为空吗?
在这里插入图片描述
原因:虽然这个AutoPtr析构了,并且判断_ptr是否为空。_ptr在构造的时候就已经指向了myclass对象的内存位置,所以第一次析构后,第二次析构的时候_ptr仍然指向该对象位置。并没有实时更新。所以这个if判空没什么卵用在这儿。
多个指针操作同一个内存空间,可以理解为并行编程的问题。

实现自己的UniquePtr

https://docs.microsoft.com/zh-cn/cpp/cpp/how-to-create-and-use-unique-ptr-instances?view=msvc-160

使用规范:只允许基础指针的一个所有者。 除非你确信需要 shared_ptr,否则请将该指针用作 POCO 的默认选项。

知识点

uniquePtr的关键点在于:

  1. 禁止了拷贝构造函数以及拷贝赋值运算符
  2. 实现了移动构造函数和移动赋值运算符
  3. unique_ptr 中的唯一数据成员是封装的指针。 这意味着,unique_ptr 与该指针的大小完全相同,不是四个字节就是八个字节。即与原始指针一样高校。
    在这里插入图片描述
    unique_ptr的所有权转换可以直接调用move进行移动构造函数/移动赋值运算符进行,转移所有权。
    内存资源所有权将转移到另一 unique_ptr,并且原始 unique_ptr 不再拥有此资源。
template <typename T>
class UniquePtr {
public:
    UniquePtr(T* ptr = nullptr) : _ptr(ptr) {}

    T* operator-> () {
        return this->_ptr;
    }

    T& operator* () {
        return *(this->_ptr);
    }

    T* get() {
        return this->_ptr;
    }

    ~UniquePtr() {
        if (_ptr != nullptr) {
            delete _ptr;
        }
    }
	//	移动构造函数,每次调用的时候,会使得当前的传入的参数的_ptr为空
    UniquePtr(UniquePtr<T>&& unqiuePtr) noexcept : _ptr(unqiuePtr._ptr) {
        std::cout << "move construct ..." << std::endl;
        unqiuePtr._ptr = nullptr;
    }

    UniquePtr& operator=(UniquePtr<T>&& uniquePtr) noexcept {
        std::cout << "move assignment..." << std::endl;
        if (this != &uniquePtr) {
            _ptr = uniquePtr._ptr;
            uniquePtr._ptr = nullptr;
        }
        return *this;
    }

	// 这样就无法直接赋值了
    UniquePtr(UniquePtr<T>& uniquePtr) = delete;
    UniquePtr<T>& operator=(UniquePtr<T>& uniquePtr) = delete;

private:·
    T* _ptr;
};

看下源码也确实是禁用了拷贝构造函数和拷贝赋值运算符:
在这里插入图片描述

int main(int argc, char* argv[]) {
    // 智能指针其实就是用指针去管理你的类;
    UniquePtr<MyClass> uniquePtr(new MyClass());
    // 下一行编译错误
    //UniquePtr<MyClass> uniquePtr2 = uniquePtr;
    UniquePtr<MyClass> uniquePtr2 = std::move(uniquePtr);
    return 0;
}

实现自己的SharedPtr / WeakPtr

概念

shared_ptr 类型是 C++ 标准库中的一个智能指针,是为多个所有者可能必须管理对象在内存中的生命周期的方案设计的。 在您初始化一个 shared_ptr 之后,您可复制它,按值将其传入函数参数,然后将其分配给其他 shared_ptr 实例。 所有实例均指向同一个对象,并共享对一个“控制块”(每当新的 shared_ptr 添加、超出范围或重置时增加和减少引用计数)的访问权限。 当引用计数达到零时,控制块将删除内存资源和自身。

采用引用计数的智能指针。 如果你想要将一个原始指针分配给多个所有者(例如,从容器返回了指针副本又想保留原始指针时),请使用该指针。 直至所有 shared_ptr 所有者超出了范围或放弃所有权,才会删除原始指针。

结合 shared_ptr 使用的特例智能指针。 weak_ptr 提供对一个或多个 shared_ptr 实例拥有的对象的访问,但不参与引用计数。 如果你想要观察某个对象但不需要其保持活动状态,请使用该实例。
在这里插入图片描述
Diagram1: p1管理MyClass,引用计数为1
Diagram2: p1 和 p2都在管理

知识点

  1. 通常shared_ptr内部实现就不是一个引用计数,而是两个引用计数,一个标识strong reference,一个标识weak reference即weak_ptr进行复制的时候的计数。
template <typename T>
class SharedPtr {
public:
    SharedPtr(T* ptr = nullptr) : _ptr(ptr), _ref_count(new int(1)){}

    SharedPtr(SharedPtr<T>& sharedPtr) : _ptr(sharedPtr._ptr), _ref_count(sharedPtr._ref_count) {
        ++(*_ref_count);
    }

    SharedPtr& operator=(SharedPtr<T>& sharedPtr) {
        if (this != &sharedPtr) {
            _release();
            _ptr(sharedPtr._ptr);
            _ref_conut(sharedPtr._ptr);
            ++(*_ref_count);
        }

        return *this;
    }

    ~SharedPtr() {
        _release();
    }

    void _release() {
        std::cout << "deconstruct...: count=" << ((*_ref_count) - 1) << std::endl;
        if (--(*_ref_count) == 0) {
            delete _ptr;
            delete _ref_count;
        }
    }

    int getCount() {
        return *_ref_count;
    }

    T* operator-> () {
        return this->_ptr;
    }

    T& operator* () {
        return *(this->_ptr);
    }

    T* get() {
        return this->_ptr;
    }

    SharedPtr(SharedPtr<T>&& unqiuePtr) noexcept : _ptr(unqiuePtr._ptr) {
        std::cout << "move construct ..." << std::endl;
        unqiuePtr._ptr = nullptr;
    }

    SharedPtr& operator=(SharedPtr<T>&& SharedPtr) noexcept {
        std::cout << "move assignment..." << std::endl;
        if (this != &SharedPtr) {
            _ptr = SharedPtr._ptr;
            SharedPtr._ptr = nullptr;
        }
        return *this;
    }

private:
    T* _ptr;
    int* _ref_count;
};

在这里插入图片描述

智能指针好文:
https://avdancedu.com/9683d88/#valine-comments

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值