37-智能指针分析

注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。

测试环境:Ubuntu 10.10

GCC版本:9.2.0

 

一、永恒的话题

1)内存泄露(臭名昭著的Bug

        -    动态申请堆空间,用完后不归还

        -    C++语言中没有垃圾回收的机制

        -    指针无法控制所指堆空间的生命周期

编程实验
内存泄露
37-1.cpp
#include <iostream>
#include <string>

using namespace std;

class Test
{
    int i;
public:
    Test(int i)
    {
        this->i = i;    //不用this区分不出来是哪个i
    }
    int value()
    {
        return i;
    }
    ~Test()
    {
    }
};

int main()
{
    for(int i=0; i<5; i++)
    {
        Test* p = new Test(i);    //没有释放申请的内存p
        
        cout << p->value() << endl;
    }
    
    return 0;
}

操作:

1) g++ 37-1.cpp -o 37-1.out编译正常,打印结果:

0
1    
2    
3    
4    

分析:

        for循环内定义局部对象指针p,申请堆内存,每次循环结束后没有释放内存,发生内存泄露,如果进程长期运行,可使用的内存越来越小,系统运行越来越慢。

 

二、深度的思考

1)我们需要什么

        -    需要一个特殊的指针

        -    指针生命周期结束时主动释放堆空间(特别需要)

        -    一片堆空间最多只能由一个指针标识(对象间赋值时,防止浅拷贝带来重复释放堆内存的危害。)

        -    杜绝指针运算和指针比较(避免指针越界和野指针)

 

三、智能指针分析

1)解决方案

    -    重载指针特征操作符->*

    -    只能通过类的成员函数重载

    -    重载函数不能使用参数

    -    只能定义一个重载函数

       智能指针本质:是个类,对象创建时,指针产生,分配内存;对象被摧毁时,调用析构函数,释放内存。

 

编程实验
智能指针
37-2.cpp
#include <iostream>
#include <string>

using namespace std;

class Test
{
    int i;
public:
    Test(int i)
    {
        cout << "Test(int i)" << endl;
        this->i = i;
    }
    
    int value()
    {
        return i;
    }
    
    ~Test()
    {
        cout << "~Test()" << endl;
    }
};

class Pointer
{
    Test* mp;     
public:
    Pointer(Test* p = NULL) //初始化指针
    {
        cout << "Pointer(Test* p = NULL)" <<endl;
        mp = p;
    }
    
    Pointer(const Pointer& obj)  
    {                           
        mp = obj.mp;//将被使用的类的堆空间,给当前类的指针,为了避免冲突
        const_cast<Pointer&>(obj).mp = NULL;//销毁obj对象中的指针(实现:一片堆空间只有一个指针)      
    }                                 
    
    Pointer& operator = (const Pointer& obj)    
    {
        if( this != &obj )     //取对象地址(效率高)
        {
            delete mp;  //从异常安全性原则考虑定义临时变量交换,退出代码调用析构函数释放赋值前当前对象中的数据
            mp = obj.mp;       
            const_cast<Pointer&>(obj).mp = NULL;
        }
        //cout << "b" <<endl
        return *this;
    }
    /* 重载-> :操作指针,最好拷贝一份指针,用拷贝的指针进行操作,以免地址丢失*/
    Test* operator -> ()
    {
        return mp;
    }
    /* 重载* :引用类型为了返回指针指向的变量本身,而不是拷贝值*/
    Test& operator * ()
    {
        return *mp;
    }
    bool isNull()
    {
        return (mp == NULL);
    }
    ~Pointer()
    {
        cout << "~Pointer()" << endl;
        delete mp;
    }
};

int main()
{
    Pointer p1 = new Test(0);  //调用Test(int i)和Pointer(Test* p = NULL)
    
    cout << p1->value() << endl; //0
    
    Pointer p2 = p1; //
    //上一行等价于Pointer p2(p1);没有调用重载的赋值操作符(=)
    cout << p1.isNull() << endl;   //1
    
    cout << p2->value() << endl;   //0。p2->value() ==> mp->value
    
    return 0;
}

操作:

1) g++ 37-2.cpp -o 37-2.out编译正常,打印结果:

Test(int i)  ==> new Test(0)
Pointer(Test* p = NULL) ==> p1构造函数   
0
1
0
~Pointer()  ==> p2析构函数
~Test()
~Pointer()  ==> p1析构函数

分析:

        智能指针实现存储一个目标类型的类指针变量,构造函数中指针指向目标对象(Test)数据堆内存。智能指针销毁时,调用析构函数,析构函数中delete目标类型指针,然后调用指针堆空间中存储的目标类型对象(Test)进行销毁,进而触发该对象(Test)的析构函数,销毁内存(间接方式销毁内存)。

 

缺陷:

        必须在智能指针中实现定义好目标类型类指针变量,不灵活。

解决办法:

        通过泛类型编程(模板技术)解决,支持更多类型!

 

int main()
{
    for(int i=0; i<5; i++)
    {
        Pointer p = new Test(i);
 
        cout << p->value() << endl;
    }   
}
智能指针类对象p是在栈上定义,并分配内存,每循环一次都会退出代码段,栈上的p被销毁,
智能指针类中的析构函数被触发,去释放申请的堆内存,防止指针泄露。

 

2) 智能指针的使用军规

只能使用指向堆空间中的对象或者变量

不要使用智能指针指向栈空间中的地址!(不用C格式的指针,{}中的都是栈空间)

 

小结

1)指针特征操作符(->*)可以被重载

2)重载指针特征符能够使用对象代替指针

3)智能指针只能用于指向堆空间中的内存

4)智能指针的意义在于最大程度的避免内存错误

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值