智能指针的发展史

1 .智能指针的发展史
智能指针的发展要从RAII说起,RAII要求,资源的有效期与持有资源的对象的生命期严格绑定,即由对象的构造函数完成资源的分配(获取),同时由析构函数完成资源的释放。在这种要求下,只要对象能正确地析构,就不会出现资源泄露问题。
(1)C++98提出auto _Ptr,auto_Ptr的设计是让两个指针指向同一块空间,但是这样会存在释放不当,出现程序崩溃的问题。
(2)在Boost库中设计了Scoped_Ptr,Shared_Ptr,Weak_Ptr这三种只能指针
(3)C++11经过进一步改进设计出了Unique_ptr,shared_ptr,Weak_ptr 这三种智能指针,Unique_ptr与相类似。
2.auto_Ptr/Scoped_Ptr/Shared_ptr/Weak_Ptr的设计思想、缺陷,以及它们各自的模拟实现?
(1) auto_Ptr的简单实现
auto_Ptr的几点注意事项:
auto_Ptr不能共享所有权
auto_Ptr不能指向数组
auto_Ptr不能作为容器的成员
不能通过赋值操作来初始化auto_Ptr

template<class T>
class auto_Ptr
{
public:
    auto_Ptr(T* ptr)
        :_ptr(ptr)
    {}

    // ap2(ap1)
    auto_Ptr(auto_Ptr<T>& ap)
        :_ptr(ap._ptr)
    {
        ap._ptr = NULL;
    }

    // ap1 = ap2
    auto_Ptr<T>& operator=(auto_Ptr<T>& ap)
    {
        if (this != &ap)
        {
            if (_ptr)
                delete _ptr;

            _ptr = ap._ptr;
            ap._ptr = NULL;
        }

        return *this;
    }

    ~auto_Ptr()
    {
        if (_ptr)
        {   
            delete _ptr;
        }
    }

    T& operator*()
    {
        return *_ptr;
    }

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

protected:
    T* _ptr;
    };

可以看到一个很明显的缺陷 当使用了构造函数和拷贝构造函数时,原来的智能指针就失效了,使用权交给了新的智能指针,这种方式叫做管理权转移,任何时候只能有一个对象指向那块区域,当有看、两个指针指向同一块区域时,程序会崩溃
这里写图片描述
(2)Boost阶段 Boost阶段提出Scoped_Ptr 守卫指针 防拷贝

SharedPtr的提出主要是建立在AutoPtr的基础上,为了防止赋值和拷贝构造,可以用两种方法解决, 其一:只声明不实现
其二:声明成私有,这样,编译时就不会报错。
Scoped_Ptr的模拟实现

template<class T>
class Scoped_Ptr
{
public:
    Scoped_Ptr(T* ptr)
        :_ptr(ptr)
    {}

    ~Scoped_Ptr()
    {
        delete _ptr;
    }

    T& operator*()
    {
        return *_ptr;
    }

    T* operator->()
    {
        return _ptr;
    }
private:
    Scoped_Ptr(const Scoped_Ptr<T>&);
    Scoped_Ptr<T>& operator=(const Scoped_Ptr<T>&);

protected:
    T* _ptr;
};

Shared_Ptr的 简单实现
相对 于AutoPtr,Shared _Ptr 解决了指针共享的所有权问题,Shared_Ptr引入了引用计数,可以被自由的拷贝和赋值。

template<class T, class Del>
class Shared_Ptr
{
public:
    Shared_Ptr()
        :_ptr(NULL)
        ,_refCount(NULL)
    {}

    Shared_Ptr(T* ptr = NULL, Del del)
        :_ptr(ptr)
        ,_refCount(new int(1))
        ,_del(del)
    {}

    ~Shared_Ptr()
    {
        if (--(*_refCount) == 0)
        {
            cout<<"delete"<<endl;
            delete _ptr;
            _del(_ptr);

            delete _refCount;
        }
    }

    Shared_Ptr(const Shared_Ptr<T, Del>& sp)
        :_ptr(sp._ptr)
        ,_refCount(sp._refCount)
    {
        ++(*_refCount);
    }

     sp2 = sp3
    Shared_Ptr<T>& operator=(const Shared_Ptr<T>& sp)
    {
        if (this != &s)
        if(_ptr != sp._ptr)
        {
            if (--(*_refCount) == 0)
            {
                delete _ptr;
                delete _refCount;
            }

            _ptr = sp._ptr;
            _refCount = sp._refCount;
            ++(*_refCount);
        }

        return *this;
    }


    int RefCount()
    {
        return *_refCount;
    }

    T& operator*()
    {
        return *_ptr;
    }

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

    T* GetPtr() const
    {
        return _ptr;
    }

protected:
    T* _ptr;
    int* _refCount;

    Del _del;
};

Shared_ptr会带来循环引用问题。Weakptr 是用来解决Shared_ptr循环引用的缺陷问题,WeakPtr是一个弱指针,weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少.
WeakPtr的模拟实现
  

template<class T>
class Weak_Ptr
{
public:
    Weak_Ptr()
        :_ptr(NULL)
    {}

    Weak_Ptr(const Shared_Ptr<T>& sp)
        :_ptr(sp.GetPtr())
    {}

    T& operator*()
    {
        return *_ptr;
    }

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

protected:
    T* _ptr;
};

虽然通过弱引用指针可以有效的解除循环引用, 但这种方式必须在程序员能预见会出现循环引用的情况下才能使用, 也可以是说这个仅仅是一种编译期的解决方案, 如果程序在运行过程中出
了循环引用, 还是会造成内存泄漏。
循环引用问题
你中有我,我中有你最后都导致双放都无法释放,最终导致内存泄漏,以双链表为例

struct ListNode
{
    Weak_Ptr<ListNode> _prev;
    Weak_Ptr<ListNode> _next;

    ~ListNode()
    {
        cout<<"~ListNode()"<<endl;
    }

};

void TestCycleRef()
{
    Shared_Ptr<ListNode> cur = new ListNode;
    Shared_Ptr<ListNode> next = new ListNode;

    cur->_next = next;
    next->_prev = cur;
}

这里写图片描述
在这里,_next和_prev都是成员变量,第一块空间要想释放必须在第二块空间被释放之后才能释放,但由于第一块空间有一个节点指向第二块空间,所以只有第一块空间释放之后第二块空间才能释放,这样就带来了循环引用问题。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基类智能指针转子类智能指针是一种在面向对象编程中常见的操作,用于将指向基类对象的智能指针转换为指向子类对象的智能指针。这种转换通常发生在需要使用子类特有的方法或属性时。 在C++中,可以使用dynamic_pointer_cast函数来进行基类智能指针到子类智能指针的转换。dynamic_pointer_cast是一个模板函数,它接受两个参数:要转换的目标类型和要转换的智能指针。 以下是一个示例代码: ```cpp #include <iostream> #include <memory> class Base { public: virtual void foo() { std::cout << "Base::foo()" << std::endl; } }; class Derived : public Base { public: void foo() override { std::cout << "Derived::foo()" << std::endl; } void bar() { std::cout << "Derived::bar()" << std::endl; } }; int main() { std::shared_ptr<Base> basePtr = std::make_shared<Derived>(); std::shared_ptr<Derived> derivedPtr = std::dynamic_pointer_cast<Derived>(basePtr); if (derivedPtr) { derivedPtr->foo(); derivedPtr->bar(); } else { std::cout << "Failed to cast basePtr to derivedPtr" << std::endl; } return 0; } ``` 在上述示例中,我们创建了一个基类Base和一个派生类Derived。然后,我们使用std::make_shared函数创建了一个指向Derived对象的基类智能指针basePtr。接下来,我们使用dynamic_pointer_cast将basePtr转换为指向Derived对象的智能指针derivedPtr。如果转换成功,我们就可以使用derivedPtr来调用Derived类中的方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值