zoukankan      html  css  js  c++  java
  • 智能指针之atuo_ptr源码剖析

    由于c++没有垃圾回收机制,像堆只能手动开辟内存,手动释放,像栈只能系统开辟,系统释放,于是智能指针出现了,它实现了内存的手动开辟,系统释放,防止了内存泄漏问题;

    我们知道, 栈对象在离开其作用域的时候, 会自动调用析构函数, 所以, 可以考虑把某一栈对象与某一堆内存绑定,且在其析构函数中释放堆内存,  那么, 在该栈对象离开作用域时, 堆内存自动释放, 这就是智能指针(本质是栈对象)的原理。  这个栈对象装得像指针一样, 所以我们称之为智能指针, 其实, 它不过就是个普通的栈对象而已。 

    在c++ 98 中只有auto-ptr,这个指针是不完善的,现在几乎都要被摒弃了,但我们还是要去了解一下他的思想,明白为何要被摒弃的呢?以及在c++11上又是如何对他如何改进的呢?

    //自动指针即auto_ptr,不同于scoped_ptr指针的是自动指针会转移使用权
    //在进行赋值或者拷贝构造之后,原来的auto_ptr会失去对所管指针的拥有权,并且将自己的指针赋为NULL
    //同时,在赋值和拷贝构造之后,原来的auto_ptr的指针会指向NULL,也是它最大的弊端之一;

    #include <iostream>
    using namespace std;


    // 简单类
    class A
    {
    public:
    void fun()
    {

    }
    };


    template<class T>

    // 类模板
    class auto_ptr
    {
    public:

    // explicit构造函数, 禁止类型转化
    explicit auto_ptr(T *p = 0) throw()
    : m_bIsOwner(p != 0), m_ptr(p)
    {
    cout << "debug1" << endl;
    }

    // owner转移
    auto_ptr(const auto_ptr<T>& y) throw()
    : m_bIsOwner(y.m_bIsOwner), m_ptr(y.release())
    {
    cout << "debug2" << endl;
    }

    // owner转移
    auto_ptr<T>& operator=(const auto_ptr<T>& y) throw()
    {
    cout << "debug3" << endl;

    if (this != &y) // 当前对象不是y对象
    {
    cout << "debug4" << endl;

    if (m_ptr != y.get()) // 当前对象绑定的地址不是y对象绑定的地址
    {
    cout << "debug5" << endl;

    if (m_bIsOwner) // 如果当前对象已经绑定堆, 则要先释放
    {
    cout << "debug6" << endl;
    delete m_ptr;
    }

    cout << "debug7" << endl;

    m_bIsOwner = y.m_bIsOwner; // 转移owner
    }
    else if (y.m_bIsOwner) // 当前对象与y绑定到同一块堆上, 且y是owner, 则把y的owner转移给当前对象
    {
    cout << "debug8" << endl;

    m_bIsOwner = true;
    }

    cout << "debug9" << endl;

    m_ptr = y.release(); // 让y不再是owner
    }

    cout << "debug10" << endl;

    return *this; // 返回当前对象的引用
    }

    // 析构函数
    ~auto_ptr()
    {
    cout << "debug11" << endl;

    if (m_bIsOwner) // 只有拥有owner属性才释放堆, 这样避免重复释放
    {
    cout << "debug12" << endl;

    delete m_ptr; // 即使m_ptr是空指针也木有关系
    }
    }

    // 重载对象的*运算符, 使得对象"看起来"像指针, 可以执行*p操作
    T& operator*() const throw()
    {
    cout << "debug13" << endl;

    return *get();
    }

    // 重载对象的->运算符
    T *operator->() const throw()
    {
    cout << "debug14" << endl;

    return get();
    }

    // 获得对象绑定的地址
    T *get() const throw()
    {
    cout << "debug15" << endl;

    return m_ptr;
    }

    // 去掉对象的owner属性
    T *release() const throw()
    {
    cout << "debug16" << endl;

    ((auto_ptr<T> *)this)->m_bIsOwner = false;
    return m_ptr;
    }

    private:
    bool m_bIsOwner; // 对象是否拥有为owner的标志
    T *m_ptr; // 对象绑定的指针
    };


    int main()
    {
    {
    cout << "------------------------------" << endl;

    // 用法错误, 因为构造函数中有explicit, 不允许类型转化
    //auto_ptr<int> p = new int(10);
    }


    {
    cout << "------------------------------" << endl;

    // ok
    auto_ptr<int> p(new int(10));
    }


    {
    cout << "------------------------------" << endl;

    // 下面代码有严重的运行期错误, 实际上是尝试delete栈上的内容
    int a = 10;
    //auto_ptr<int> p(&a);
    }


    {
    cout << "------------------------------" << endl;

    auto_ptr<int> p(new int(10));

    // 错误, p虽然"看似像"指针, 其本质是对象, delete p;是未定义行为
    //delete p;
    }


    {
    cout << "------------------------------" << endl;

    int *q = new int(10);
    auto_ptr<int> p(q);

    // 错误, q释放一次, p释放一次, 重复释放啊
    //delete q;
    }


    {
    cout << "------------------------------" << endl;

    auto_ptr<int> p0;

    // 有debug3的打印, 但没有debug4, 知道原因了吧
    p0 = p0;
    }


    {
    cout << "------------------------------" << endl;

    auto_ptr<int> p0(new int(10));

    // 注意, 这是初始化, 不是复制, 所以不会有debug3的打印
    auto_ptr<int> p1 = p0;
    }


    {
    cout << "------------------------------" << endl;

    auto_ptr<int> p0(new int(10));
    auto_ptr<int> p1;

    // 注意, 这才是赋值, 所有有debug3, debug4, debug5, debug7, debug9, debug10的打印
    // 为什么没有debug6呢? 因为当前对象p1还不是owner
    p1 = p0;
    }


    {
    cout << "------------------------------" << endl;

    auto_ptr<int> p0(new int(10));
    auto_ptr<int> p1(new int(20));

    // 有debug6的打印, 因为当先释放p1绑定的对象, 否则内存又泄露了啊
    p1 = p0;
    }


    {
    cout << "------------------------------" << endl;

    auto_ptr<int> p0(new int(10));

    // 把owner转给p1
    auto_ptr<int> p1(p0);

    // 终于见到你了, debug8
    p0 = p1;
    }

    {
    cout << "------------------------------" << endl;

    auto_ptr<int> p(new int(10));

    // 见到你了, debug13
    cout << *p << endl;
    }


    {
    cout << "------------------------------" << endl;

    auto_ptr<A> p(new A());

    // 终于见到你了, debug15
    p->fun();
    }


    {
    cout << "------------------------------" << endl;

    auto_ptr<int> p0(new int(10));
    auto_ptr<int> p1(p0);
    auto_ptr<int> p2(p1);

    // 实际上, p3才是最后的winner, 才是最后的owner, 所以释放堆的重任在p3身上
    auto_ptr<int> p3(p2);
    }


    {
    cout << "------------------------------" << endl;

    // oh, my god, 内存泄露, 本来要delete [] q; 现在析构函数只执行delete q;
    int *q = new int[3];
    auto_ptr<int> p(q);
    }


    {
    cout << "------------------------------" << endl;

    // oh, my god, 内存泄露, 本来要delete [] q; 现在析构函数只执行delete q;
    int *q = new int[3];
    auto_ptr<int> p(q);

    // 已经说过, 下面语句会造成内存重复释放
    //delete q;
    }


    // 最后说明一下, auto_ptr不适合做容器的元素, 这一点我们以后会再次讨论到


    return 0;

    }

    原文链接:https://blog.csdn.net/stpeace/article/details/45155487

    测试用例及其好的一篇博客,思路很清晰

  • 相关阅读:
    Python语言简介以及特点
    计算机组成原理简述
    Python中的dict字典的用法
    Python3中IO文件操作的常见用法
    Python中的装饰器的使用及固定模式
    Python中的解决中文字符编码的问题
    Python中常见字符串去除空格的方法总结
    Python中print函数中中逗号和加号的区别
    当引用了Properties.Settings后,如果执行的时候,出现"配置系统无法初始化" 或者 某某节点不正确
    修改IP的方法(C#)
  • 原文地址:https://www.cnblogs.com/xcb-1024day/p/11331117.html
Copyright © 2011-2022 走看看