第7章 Page442~446 7.8.9智能指针

指向堆内存的指针,很容易忘了释放:

int foo()
{
    int* p = new int(9);
    cout << *p << endl;
    return *p;
}

为什么要用指针

使用堆数据有以下几个目的(也可称为作用)如表7-18所列

表7-18 使用堆数据的常见目的
使用堆数据的目的作用说明示例附加说明
数据需要拥有超出当前代码块的生命周期多线程时传递给其他线程还未学习线程,暂不说明

在特定时机下初始化全局指针,例如:

int* p//全局变量

void foo()

{

        p = new int(9);

}

通常不推荐使用全局数据

作为全局容器的指针元素,例如:

list <int*> l; //全局容器

void foo()

{

        int* p = new int(9);

        l.push_back(p);

}

同上,并且通常也不太推荐在容器中存储基本类型数据的指针
需要传递到其他函数,由其他函数删除它在单线程的情况下,这种设计不太好,违背谁创建谁释放的原则
数据的状态表达,需要有“空”或“无”这个状态比如让一个人心里默想一个整数,然后还要有一个状态,表示他大脑防控,什么数都没想。光使用int无法表达这个状态,如果使用"int* p",可以让p为nullptr时表示放空状态指针为空的状态很常见,但通常不会仅仅为了多一个状态表达,而改为使用指针
所要使用的内存很大,必须使用堆内存

通常一个程序可用堆空间,要大于可用占空间。

例如下面这行代码,可能会让程序挂掉:

int a[665536]

改为,使用对空间则问题不大:

int* pa = new [665536];

推荐使用标准库容器,如array,vector, list等
需要在运行期才能决定所要分配的内存大小

int n;

cin >> n;

int* pa = new [n];

同上

直接给出的结论:

使用C++编写程序,如果没有用“面向对象”,则很大程度上不必要也不应该通过裸指针手工分配堆内存。

两个可怕的错误:

int foo()
{
    int* p = new int(9);
    cout << *p << endl;
    delete p; //我释放了哦

    return *p;
}

return时访问已经被释放的“*p”

int foo()
{
    int* p = new int;
    if(...)
    {
        *p = 10;
        return *p;
    }
    else
    {
        switch(...)
        {
            case 1:
                return 20;
            default:
                ...
        }
    }
    delete p;
    return 0;
}

函数中有一堆return,每个都可以结束函数,但除了最后一个,其他几个函数结束之前,都没有释放p。

怎么办?最简单的办法是,将例子中堆数据,都应该改成栈数据。

但是以后学习面向对象时,许多时候对象必须使用new创建,一不小心忘了释放,又该怎么办?有没有办法让一个对象在堆中创建,然后又会自动释放呢?

方法就是在堆中创建一个对象(称为po),然后在栈中也创建一个对象(称为Killer),然后把po交给killer来管理。

po当然可以就是“ int* ”这样简单的指针,

为了更清楚地观察对象的释放,我们为它定制下面这样的结构

然后,“O”类型的对象,交给另一个类Killer管理

对象释放时,会自动调用析构函数。定义一个Killer的栈变量,栈变量会自动释放,释放时会自动调用其析构函数,我们让析构函数负责删除po:

这个版本的“foo()”函数,创建了名为po的堆对象,后面却没有delete po的显式代码,但内存也没有泄露。因为k是栈对象,会自动释放,并会在临死之前,无情地将po干掉。

foo()运行效果

Killer的缺点:

killer管理的是“O*” ,如果要管理“int *”或其他,就得重写一个管理者

解决方案:

第一,C++有模板技术,可以只写一份代码,就能管理各种类型指针;第二,万一代码很长,程序员用着指针po,用着用着,就忘了之前将它托管给某个Killer的是,于是程序员很负责任地手工删除它:比如:

运行效果:可以看到内存被释放两次

怎么办?解决办法是:干脆不要po这个对象,在构建Killer对象是,直接new出一个无名对象作为入参:

运行效果:

至此还不能满意,因为好好的“po->HaHa()”不得不写成“k.po->HaHa”,有没有办法直接写作“k->HaHa()”呢?在C++中可以通过为Killer重载“->”这个操作符来实现

重载“->”的Killer例子:

事实上,C++已经为我们提供了很好的实现,术语上当然不是血腥的“Killer”,而是智能指针“smart pointer(智能指针)”,不管叫什么,一定要记得它是“管理者”被它管着的,称为“raw pointer(裸指针)”

智能指针分两种:

根据管理方式,C++智能指针又分成两种:一种是独占式智能指针。即一个裸指针,在同一个时间点,只允许有一个管理者。另一种是共享式智能指针,允许一个裸指针被多个智能指针同时管理。

标准库智能指针包含在“<memory>”头文件中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值