~~~~我的生活,我的点点滴滴!!
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。
总的来说,关于究竟在堆上,还是在栈上创建对象,首要考虑你所需要的生命周期。当性能真正成为瓶颈的时候,才去考虑性能的问题。
堆和栈是提供给开发者的两个不同的工具,不存在一个放之四海而皆准的规则告诉你,一个对象必须放在堆中还是在栈中。选择权在开发
者手中,决定权在开发者的经验中。