c++ 局部对象指针及到底是在堆上创建的对象还是在栈上创建的?

~~~~我的生活,我的点点滴滴!!


1、局部对象指针


我们知道在局部创建的对象,当离开函数体时系统会自动销毁,但是局部指针对象是不会的,但是很多时候我们把这个局部指针对象

添加到一个容器中后我们并没有去delete掉这个对象指针,不是有句老话“你new了,你就需要delete”,但是我们并没有,这是不

是一个悖论了?这会不会产生内存泄露了?


问题一:是否是悖论?

答案:不是的。

当我们在局部创建了一个对象指针后,我们添加到某个容器中存下来了,我们会在以后这个容器不需要的时候去释放里面的内容,

所以他只是推迟了delete的时间,这并不会影起任何问题(那个局部对象指针我们访问不到,就不会出现任何访问越界的情况)。

看下面代码:

void func1()
{
    //首先定义一个A类型的对象
    A a(1);
    //使用pArray对象中的成员函数将此对象加入到容器中
    pArray.Add(&a);
}

这种你添加到容器中也没有用,他出函数时已经析构了。可以如下修改:

void func2()
{
    //首先声明一个A类型的对象
    A* a;
    //使用pArray对象中的成员函数GetAt()将A类型的对象取出
    for(int i; i < pArray.GetSize();i++)
    {
        a = (A*)pArray.GetAt(i);
        //使用A中的数据进行相关的操作代码。***此时也可以使用delete释放指针指向的内存区块,防止内存泄露***当然是后面一种方法时才用到,暂时无视之。
        ...
    }
     
}

问题二:是否会内存泄露?

答案:不会的。

内存泄露大多时候出现在两种情况:

函数返回一个局部的对象或引用,因为局部对象在出了函数体就会被析构掉,你返回的那个地址里面已经没有任何内容了。局部

函数指针,你new完后,你并没有添加到任何别的容器中或者你自己在函数体最后delete掉,那么这块内存谁也不能访问了,

是他依然存在于你的程序内存块中,也许对于现在设备内存这大的情况下,如果这个函数调用不频繁不会出太大问题,但是总有

一天还是会出现的,所以要注意这种情况。


2、那到底什么情况下对象在堆上,什么情况在栈上了?


逗子大神分析的很透彻,如果需要在堆上创建对象,要么使用new运算符,要么使用malloc系列函数,这点没有异议。

真正有异议的是下面的代码:

Object obj;

此时,obj是在栈上分配的吗?

要回答这个问题,我们首先要理解这个语句是什么意思。这个语句就是代表着,在栈上创建对象吗?其实,这行语句的含义是,

使对象obj具有“自动存储(automatic storage)”的性质。所谓“自动存储”,意思是这个对象的存储位置取决于其声明所

在的上下文。如果这个语句出现在函数内部,那么它就在栈上创建对象。

如果这个语句不是在函数内部,而是作为一个类的成员变量,则取决于这个类的对象是如何分配的。考虑下面的代码:

class Class
{
    Object obj;
};
 
Class *pClass = new Class;

指针pClass所指向的对象在堆上分配空间。因为Object obj;语句的含义是“自动存储”,所以,pClass->obj也是在堆上创建的。


理解了这一点,再来看下面的语句:

Object *pObj;
pObj = new Object;

Object *pObj;代表,指针pObj是自动存储的,仅此而已,没有任何其它含义。而下面一行语句则指出,这个指针所指向的对象是

在堆上面分配的。如果这两行语句出现在一个函数内部,意味着当函数结束时,pObj会被销毁,但是它指向的对象不会。因此,

为了继续使用这个对象,通常我们会在函数最后添加一个return语句,或者使用一个传出参数。否则的话,这个在堆上创建的对

象就没有指针指向它,也就是说,这个对象造成了内存泄露。并不是说指针指向的对象都是在堆上创建的。下面的代码则使用指针

指向一个在栈上创建的对象:


Object obj;
Object *pObj = &obj;

至此,我们解释了函数内部的变量和成员变量。还有两类变量:全局变量和static变量。它们即不在堆上创建,也不在栈上创建。

它们有自己的内存空间,是除堆和栈以外的数据区。也就是说,当Object obj即不在函数内部,又不是类的成员变量时,这个对

象会在全局数据段创建,同理适用于static变量。对于指针Object *pObj;,如果这个语句出现在函数内部或类的成员变量,正

如我们前面所说的,这个指针是自动存储的。但是,如果这个语句是在类的外部,它就是在全局数据段创建的。虽然它指向的

对象可能在堆上创建,也可能在栈上创建。

堆和栈的区别在于两点:

1.生命周期
2.性能

第一点才是我们需要着重考虑的。由于栈的特性,如果你需要一个具有比其所在的上下文更长的生命周期的变量,只能在堆上创建它。

所以,我们的推荐是:只要能在栈上创建对象,就在栈上创建;否则的话,如果你不得不需要更长的生命周期,只能选择堆上创建。

这是由于在栈上的对象不需要我们手动管理内存。有经验的开发人员都会对内存管理感到头疼,我们就是要避免这种情况的发生。

总的来说,我们更多推荐选择在栈上创建对象。

但是,有些情况,即便你在栈上创建了对象,它还是会占用堆的空间。考虑如下代码:

void func
{
    std::vector v;
}

对象v是在栈上创建的。但是,STL 的vector类其实是在堆上面存储数据的(这点可以查看源代码)。因此,只有对象v本身是在栈上的,

它所管理的数据(这些数据大多数时候都会远大于其本身的大小)还是保存在堆上。关于第二点性能,有影响,不过一般可以忽略不计。

确切的说,一般情况下你不需要考虑性能问题,除非它真的是一个问题。


首先,在堆上创建对象需要追踪内存的可用区域。这个算法是由操作系统提供,通常不会是常量时间的。当内存出现大量碎片,或者几乎

用到 100% 内存时,这个过程会变得更久。与此相比,栈分配是常量时间的。其次,栈的大小是固定的,并且远小于堆的大小。所以,如

果你需要分配很大的对象,或者很多很多小对象,一般而言,堆是更好的选择。如果你分配的对象大小超出栈的大小,通常会抛出一个异

常。尽管很罕见,但是有时候也的确会发生。有关性能方面的问题,更多出现在嵌入式开发中:频繁地分配、释放内存可能造成碎片问题。

现代操作系统中,堆和栈都可以映射到虚拟内存中。在 32 位 Linux,我们可以把一个 2G 的数据放入堆中,而在 Mac OS 中,栈可能会

限制为 65M。

总的来说,关于究竟在堆上,还是在栈上创建对象,首要考虑你所需要的生命周期。当性能真正成为瓶颈的时候,才去考虑性能的问题。

堆和栈是提供给开发者的两个不同的工具,不存在一个放之四海而皆准的规则告诉你,一个对象必须放在堆中还是在栈中。选择权在开发

者手中,决定权在开发者的经验中。






环境:Windows XP S3、VC++ 6.o 目的:学习C++程序开发语言 使用步骤:下载之后,双击.dsw文件即可打开该示例工程 说明: 在学习《Thinking in C++》一书关于数组声明与定义时,Bruce说如果这样声明一个数组: int b[6] = {0}; Here, the compiler will use the first initializer for the first array element, and then use zero for all the elements without initializers.(意思是说如果这样声明并且定义一个数组,那么编译器会把0赋给第一个数组元素,其它五个元素会赋值0).于是我使用class声明一个类型Test。在这个类中有一个成员方法叫getArray(),在该方法中使用以上方式声明一个数组,然后返回数组的指针,然后在另一个成员方法showPointerOfArray(int*)接收传过来的int指针,在这个方法操作数组。 但是在运行时没有出现我想要的结果,于是其它的方法中测试这样声明方式,却是运行正确的。于是让我很纳闷?带这个问题与本中心庄鹏飞老师讨论之后,发现原来我没有搞清楚在C++指针分为指针指针。参见int* Test::getArray()方法中关于数组的声明以及本人非常详细的说明,那么我想会给学习C++编程的人员带来收获。 结论:C++不是纯粹的OO语言,这是bruce说的。本人在学习过程中确实感觉C++这种语言比Java难得多。不像Java那么直观易学,这可能也就是为什么世界上所有程序员中有20%左右的人是Java程序员,而不是C++程序员的原因吧。 另外,本人使用QT的g++编译器编译通过了,因为是使用记事本手写的,所以完全是Java的书写风格^_^ 把它搞成VC++的工程是为了大家方便学习。。。 学习对象:希望编写效率高于Java应用的程序员。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值