智能指针

简介:

@[TOC]

问题

对于动态开辟的空间,虽然可以通过异常来规避某些内存泄漏的可能,但是这种通过异常的方式太过于麻烦,而面向对象语言有个特性:自动获取资源,生命周期结束自动释放资源。----也就是RAII思想

那么对于动态开辟指针我们也可以基于RAII思想,包装这个指针----也就是智能指针

RAII

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。

**在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。**

借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

  1. 不需要显式地释放资源。
  2. 采用这种方式,对象所需的资源在其生命期内始终保持有效

c++98 智能指针

std::auto_ptr

管理权转移,一但出现指针赋值或者拷贝,将指针的内容进行转换,同时赋值为null的行为

c++11智能指针

std::unique_ptr

这款的智能指针不允许拷贝,也不允许赋值

//唯一智能指针
    template<class T, class D>
    class unique_ptr
    {
        typedef unique_ptr<T, D> Self;
        typedef T& Ref;
        typedef T* Ptr;
    public:
        unique_ptr(T* ptr)
            :_ptr(ptr)
        {

        }
        //c++98实现un_ique
        //1. 将拷贝构造函数和拷贝赋值重载 只声明不实现,但是不法杜绝外部显示定义
        //2. 声明在private区域

        //c++11实现un_ique
        //通过关键字 强制不生成拷贝构造函数和拷贝赋值重载
        unique_ptr(const Self& uq) = delete;//强制不生成拷贝构造函数
        Self& operator=(const Self& uq) = delete;//强制不生成拷贝赋值重载
        ~unique_ptr()
        {
            D()(_ptr);
            _ptr = nullptr;
        }
    private:
        Ptr _ptr;
    };

std::shared_ptr

这款指针指针允许正常指针的任何操作

//支持计数和拷贝,赋值等
    template<class T>
    class Shared_ptr
    {
        typedef  Shared_ptr<T> Self;
        typedef T& Ref;
        typedef T* Ptr;
        
    public:
        template<class T>
        friend class Weak_ptr;
        void Release()//减计数
        {
            if ( _pcnt&&--(*_pcnt)==0 && _ptr)
            {
                std::cout << "delete" << _ptr << " " << "~shard_ptr()" << std::endl;
                delete _ptr;
                _ptr = nullptr;
                delete _pcnt;
                _pcnt = nullptr;
            }    
        }
        void Destory()//直接销毁
        {
            if (_pcnt && *_pcnt >= 1 && _ptr)
            {
                std::cout << "delete" << _ptr << " " << "~shard_ptr()" << std::endl;
                delete _ptr;
                _ptr = nullptr;
                delete _pcnt;
                _pcnt = nullptr;

            }
        }
        Shared_ptr() = default;
        Shared_ptr(Ptr ptr)
            :_ptr(ptr),
            _pcnt(new int(1))
            
        {
            
            
        }
        Shared_ptr(const Self& sp)
            :_ptr(nullptr),
            _pcnt(nullptr)
        {

            _ptr = sp._ptr;
            _pcnt = &(++(*sp._pcnt));
        }
        Shared_ptr( const  Weak_ptr<T>& wp)
            :_ptr(nullptr),
            _pcnt(nullptr)
        {
            _ptr = wp._ptr;
            _pcnt = &(++(*wp._pcnt));
        }


    
        Self& operator=(const Self& sp)
        {
            //比较this指针的缺陷:不同对象的_ptr可以相同
            if (_ptr!=sp._ptr)
            {
                Release();
                _ptr = sp._ptr;
                _pcnt = &(++(*sp._pcnt));
            }
            return *this;
        }
        Self& operator=( const Weak_ptr<T>& wp)
        {
            //比较this指针的缺陷:不同对象的_ptr可以相同
            if (_ptr != wp._ptr)
            {
                Release();
                _ptr = wp._ptr;
                _pcnt = &(++(*wp._pcnt));
            }
            return *this;
        }
        Ref operator*()
        {
            return *_ptr;
        }
        Ptr operator->()
        {
            return &*_ptr;
        }
        bool operator==(const Self& sp)
        {
            return _ptr == sp._ptr;
        }
        bool operator!=(const Self& sp)
        {
            return _ptr != sp._ptr;
        }
        bool operator==(const Weak_ptr<T>& wp)
        {
            return _ptr == wp._ptr;
        }
        bool operator!=(const Weak_ptr<T>& wp)
        {
            return _ptr != wp._ptr;
        }


        ~Shared_ptr()
        {
            Release();
        }
    private:
        Ptr _ptr = nullptr;
        int* _pcnt = nullptr;
    
        

    };

std::weak_ptr

除了不参与内存的释放,其它与shared_ptr没有区别

//弱指针,不参与内存的释放,也不参与计数
    //默认的析构函数就可以完成不参与内存的释放
    template<class T>
    class Weak_ptr
    {
        typedef  Weak_ptr<T> Self;
        typedef T& Ref;
        typedef T* Ptr;

    public:
        template<class T>
        friend class Shared_ptr;
        Weak_ptr() = default;
        Weak_ptr(Ptr ptr)
            :_ptr(ptr),
            _pcnt(new int(1))
        {
            
        };
        void Release()//减计数
        {
            if (--(*_pcnt) == 0 && _ptr)
            {
                std::cout << "delete" << _ptr << " " << "~shard_ptr()" << std::endl;
                delete _ptr;
                _ptr = nullptr;
                delete _pcnt;
                _pcnt = nullptr;
                
            }
        }
        void Destory()//直接销毁
        {
            if (_pcnt && *_pcnt >= 1 && _ptr)
            {
                std::cout << "delete" << _ptr << " " << "~shard_ptr()" << std::endl;
                delete _ptr;
                _ptr = nullptr;
                delete _pcnt;
                _pcnt = nullptr;

            }
        }
        Weak_ptr(const My_SP::Shared_ptr<T>& sp)
        {
            try
            {
                if (expired(sp))
                {
                
                    throw "对象已过期";
                }
            }
            catch (const char*str)
            {
                std::cout << __LINE__ <<" " << str << std::endl;
            }
            _ptr = sp._ptr;
            _pcnt = sp._pcnt;
        }
        Weak_ptr(const Self& wp)
        {
            try
            {
                if (expired(wp))
                {

                    throw "对象已过期";
                }
            }
            catch (const char* str)
            {
                std::cout << __LINE__ << " " << str << std::endl;
            }
            _ptr = wp._ptr;
            _pcnt = wp._pcnt;
        }
        Self& operator=(const Self& sp)
        {
            //比较this指针的缺陷:不同对象的_ptr可以相同
            if (_ptr != sp._ptr)
            {
                _ptr = sp._ptr;
                _pcnt = sp._pcnt;
            }
            return *this;
        }
        //检测是否过期
        bool expired(const My_SP::Shared_ptr<T>& sp)
        {
            return !(*(sp._pcnt) >= 1 && sp._ptr);
        }
        bool expired(const Self& wp)
        {
            return !(wp._ptr);
        }

        bool operator==(const Self& wp)
        {
            return _ptr == wp._ptr;
        }
        bool operator!=(const Self& wp)
        {
            return _ptr != wp._ptr;
        }
        bool operator==(const My_SP::Shared_ptr<T>& sp)
        {
            return _ptr == sp._ptr;
        }
        bool operator!=(const My_SP::Shared_ptr<T>& sp)
        {
            return _ptr != sp._ptr;
        }
        Ref operator*()
        {
            return *_ptr;
        }
        Ptr operator->()
        {
            return _ptr;
        }

    private:
        Ptr _ptr=nullptr;
        int* _pcnt=nullptr;
    };

为什么存在weak_ptr

shared_ptr在某些环境会出现循环引用问题

观察下列代码

template<class T>
class Node
{
    Node(const T& data = T())
        :_data(data),
    {
        ;
    }

    T _data;
    My_SP::Shared_ptr<int> _next;
    My_SP::Shared_ptr<int> _prev;

};
void T()
{
    
    My_SP::Shared_ptr<Node<int>> A;
    My_SP::Shared_ptr<Node<int>> B;
    A->_next = B;
    B->_prev =A;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y2eKxeNE-1665135358975)(./%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88.assets/image-20221007160921468-1665130162683-1.png)]

    template<class T>
    class Weak_ptr
    {
        typedef  Weak_ptr<T> Self;
        typedef T& Ref;
        typedef T* Ptr;

    public:
        template<class T>
        friend class Shared_ptr;
    
        Weak_ptr(Ptr ptr)
            :_ptr(ptr),
            _pcnt(new int(1))
        {
            //这里一定要初始化_pcnt的指向对象值为1,
            //有需求
        };
        void Release()//减计数
        {
            if (--(*_pcnt) == 0 && _ptr)
            {
                delete _ptr;
                _ptr = nullptr;
                delete _pcnt;
                _pcnt = nullptr;
                std::cout << "delete" << _ptr << " " << "~shard_ptr()" << std::endl;
            }
        }
        Weak_ptr(const My_SP::Shared_ptr<T>& sp)
        {
            try
            {
                if (expired(sp))
                {
                
                    throw "对象已过期";
                }
            }
            catch (const char*str)
            {
                std::cout << __LINE__ <<" " << str << std::endl;
            }
            _ptr = sp._ptr;
            _pcnt = sp._pcnt;
        }
        Weak_ptr(const Self& wp)
        {
            try
            {
                if (expired(wp))
                {

                    throw "对象已过期";
                }
            }
            catch (const char* str)
            {
                std::cout << __LINE__ << " " << str << std::endl;
            }
            _ptr = wp._ptr;
            _pcnt = wp._pcnt;
        }
        Self& operator=(const Self& sp)
        {
            //比较this指针的缺陷:不同对象的_ptr可以相同
            if (_ptr != sp._ptr)
            {
                _ptr = sp._ptr;
                _pcnt = sp._pcnt;
            }
            return *this;
        }
        //检测是否过期
        bool expired(const My_SP::Shared_ptr<T>& sp)
        {
            return !(*(sp._pcnt) >= 1 && sp._ptr);
        }
        bool expired(const Self& wp)
        {
            return !(wp._ptr);
        }

        bool operator==(const Self& wp)
        {
            return _ptr == wp._ptr;
        }
        bool operator!=(const Self& wp)
        {
            return _ptr != wp._ptr;
        }
        bool operator==(const My_SP::Shared_ptr<T>& sp)
        {
            return _ptr == sp._ptr;
        }
        bool operator!=(const My_SP::Shared_ptr<T>& sp)
        {
            return _ptr != sp._ptr;
        }
        Ref operator*()
        {
            return *_ptr;
        }
        Ptr operator->()
        {
            return _ptr;
        }

    private:
        Ptr _ptr=nullptr;
        int* _pcnt=nullptr;
    };

指针指针简化版的list

  1. Node内部使用weak_ptr来避免死循环释放问题
  2. 为了避免节点被局部变量无意间释放,使用weak_ptr来避免出现问题
  3. 对于节点的释放可以通过weak_ptr直接使用Destory函数销毁而不是减计数
  4. 在需要使用智能指针的地方,像list这种,要优先考虑weak_ptr
#pragma once
#include "SmartPtr.h"
#include <assert.h>
namespace My_list
{
    template<class T>
    struct listNode
    {
        listNode(const T data = T())
            :_data(data)
        {

        }
        listNode(const  listNode<T>& node)
        {
            _data = node._data;
            _next = node._next;
            _prev = node._prev;
        }
        T _data;
        My_SP::Weak_ptr<listNode<T>> _next ;
        My_SP::Weak_ptr<listNode<T>> _prev ;

    };

    template<class T>
    class list
    {
        typedef listNode<T> Node;
        typedef list<T> Self;
    public:
        list()
        :_head(new Node),
         _sz(0)
        {
            
            _head->_prev = _head;
            _head->_next = _head;

        }
        list(const Self& lt)
            :_head(lt._head),
            _sz(lt.sz)
        {

        }
        void Push(const T &data)
        {    
            My_SP::Weak_ptr<Node> newnode = new Node(data);
            //必须要用Weak_ptr,
            My_SP::Weak_ptr<Node> tmp = _head->_prev;
            tmp->_next = newnode;
            newnode->_prev = tmp;
            newnode->_next = _head;
            _head->_prev = newnode;
            ++_sz;
        }
        void  erase(My_SP::Weak_ptr<Node>  pos)
        {
            
            if (_sz == 0)
            {
            
            }
            else
            {
                My_SP::Weak_ptr<Node>  prevnode = pos->_prev;
                My_SP::Weak_ptr<Node>   nextnode = pos->_next;
                prevnode->_next = nextnode;
                nextnode->_prev = prevnode;
                --_sz;
            }
            pos.Destory();
        }
        bool empty()
        {
            return _sz == 0;
        }
        void Destory(My_SP::Weak_ptr<Node> head)
        {

            if (head->_next == _head)
            {
                erase(head);
                return;
            }
            Destory(head->_next);
            erase(head);
        }
        ~list()
        {
            Destory(_head);
        }
        void Print()
        {
             My_SP::Weak_ptr<Node> pos = _head->_next;
            while (pos != _head)
            {
                cout << pos->_data << endl;
                pos = pos->_next;
            }
            cout << endl;
        }
    private:
        My_SP::Shared_ptr<Node> _head;
        size_t _sz = 0;
    };


}

在这里插入图片描述

类型转换

c语言中的类型转换

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型
转换和显式类型转换。

  1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
  2. 显式类型转化:需要用户自己处理

缺陷:

  1. 转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换
  2. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
  3. 显式类型转换将所有情况混合在一起,代码不够清晰

C++11类型转换

static_cast

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换:2种数据类型是不同的如:表示数据大小的和表示人的数据等

reinterpret_cast

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型 ,但是转换不改变之前的属性即const还是const,非const还是非const

const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值

dynamic_cast

用于基类与派生类间的转换,这种转换有2种:向上和向下转换

向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)

向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)

注意:

  1. dynamic_cast只能用于父类含有虚函数的类
  2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
为什么存在向下转换?

当给定的对象是一个基类的指针或者引用时,我们无法确定指向的是基类还派生类,这种可以通过dynamic_cast进行区分。

当基类指针指向基类时,向派生类转换时dynamic_cast会转换失败,返回nullptr

当基类指针指向派生时,向派生类转换时dynamic_cast会转换成功。

class A
{
public :
virtual void f(){}
};
class B : public A
{};
void fun (A* pa)
{
// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
B* pb1 = static_cast<B*>(pa);
B* pb2 = dynamic_cast<B*>(pa);
cout<<"pb1:" <<pb1<< endl;
cout<<"pb2:" <<pb2<< endl;
}
相关文章
|
4月前
|
安全 程序员 编译器
C++中的RAII(资源获取即初始化)与智能指针
C++中的RAII(资源获取即初始化)与智能指针
57 0
|
4月前
|
安全 程序员 C++
C++中的智能指针:从原始指针到现代内存管理
C++中的智能指针:从原始指针到现代内存管理
37 0
|
4月前
|
C++
C++:一文读懂智能指针
C++:一文读懂智能指针
82 0
|
4月前
|
消息中间件 Kubernetes NoSQL
智能指针的种类以及使用场景
智能指针的种类以及使用场景
|
4月前
|
安全 C++
c++11新特性——智能指针详解
c++11新特性——智能指针详解
|
4月前
|
安全 C++ 容器
C++中的智能指针:自动内存管理的利器
C++中的智能指针:自动内存管理的利器
62 0
|
4月前
|
设计模式 Rust Java
【一起学Rust | 设计模式】习惯语法——默认特质、集合智能指针、析构函数
【一起学Rust | 设计模式】习惯语法——默认特质、集合智能指针、析构函数
81 0
|
4月前
|
存储 Rust C++
C++智能指针
【2月更文挑战第14天】介绍C++智能指针
43 0
|
4月前
|
算法 安全 C++
C++ Effective Modern Pointer (智能指针模块)
C++ Effective Modern Pointer (智能指针模块)