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

本文探讨了局部对象指针在不同上下文中如何管理内存,包括何时使用new与delete,以及对象在栈和堆上的区别。重点讲解了生命周期与性能两方面的考量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


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。

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

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

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






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值