首页新闻找找看学习计划

c++对象内存分配的问题,如何确定在堆上还是在栈上?

0
[已解决问题] 解决于 2015-03-06 01:26

c++中对象内存放堆上还是在栈上是不是根据对象实例化的方法,如:
A a; //在栈中分配内存
A * a = new A(); //动态内存分配,在堆上

要是这样,如果我尽量不用指针的话(这样程序风格似乎更加清晰一些),那是不是对象都分配到栈上了?这有什么弊端吗?栈的空间是不是很有限?是不是为了栈空间的问题我们必须尽量多用new分配内存呢?类成员是不是也要多用指针呢?

Patrickz10的主页 Patrickz10 | 初学一级 | 园豆:7
提问于:2015-03-05 16:00
< >
分享
最佳答案
0

天方 高手之赐教,对该问题的种种疑问已烟飞云散,http://q.cnblogs.com/q/70189/

该结帖了,在这里还是一样谢谢诸位的热心讨论

Patrickz10 | 初学一级 |园豆:7 | 2015-03-06 01:26
其他回答(3)
0

栈内存是编译时可以确定的内存,对内存是运行时动态申请的内存。

仅此而已,

A a; //在栈中分配内存(在栈上一个变量a指向堆中的一个A类型的对象(没有赋值的话是null吧?好久没写c++了不清楚))
A * a = new A(); //动态内存分配,在堆上(在栈上一个指针变量a,指向在堆上的一个A类型的对象)

对象是不会在栈上的吧,对象只会在堆里,只有基础类型的变量才会在栈上,.net是这样的,C++不清楚了

吴瑞祥 | 园豆:28729 (高人七级) | 2015-03-05 16:16

你说:“A a; //在栈上一个变量a指向堆中的一个A类型的对象”,应该不是吧?如果这样,那和A * a = new A();有什么差别呢?它如何做到自动回收内存呢?如果两个都是这么分配内存,那c++为什么不干脆都支持自动回收内存呢?

支持(0) 反对(0) Patrickz10 | 园豆:7 (初学一级) | 2015-03-05 16:53

@Patrickz10: 在c++里A a;这时候a不会为null吗?不记得了

主要问题还是在于作用域,栈里的内存是在当前栈里的,一处当前栈就没了。

堆里的内存,你不去释放他,他就一直在那里

支持(0) 反对(0) 吴瑞祥 | 园豆:28729 (高人七级) | 2015-03-05 17:06

@吴瑞祥: A a;a会调用默认构造函数,当然不会为null了,只有指针才可能为null啊

支持(0) 反对(0) Patrickz10 | 园豆:7 (初学一级) | 2015-03-05 17:21
1

你的理解有些偏差,学习下这篇文章:http://blog.163.com/dingmz_frcmyblog/blog/static/21730402320137994451342/,加深对内存分配的理解(注意 alloca 这个函数,它实际上是在栈上分配空间)。

 

1、栈的大小是有限制的,可以修改;

2、分配在堆上,还是分配在栈上,要综合考虑你的程序的需求;

Launcher | 园豆:45040 (高人七级) | 2015-03-05 16:19

学到了

支持(0) 反对(0) 吴瑞祥 | 园豆:28729 (高人七级) | 2015-03-05 16:20

我想先了解最基本的语法c++是如何分配内存,然后才了解alloca等那些函数或具体平台如何做啊,不知道我写的

A a; //在栈中分配内存
A * a = new A(); //动态内存分配,在堆上

这两句话对不对哦?

支持(0) 反对(0) Patrickz10 | 园豆:7 (初学一级) | 2015-03-05 16:57

@Patrickz10: 第一个语句没问题,第二个语句有问题,因为 new 是个操作符语法,可以重载,然后通过调用 alloca 来在栈上分配。

支持(0) 反对(0) Launcher | 园豆:45040 (高人七级) | 2015-03-05 17:12

@Launcher: 哦,对,我的new是没有重载的。我想知道一个问题,如果有一个自定义类型中有一个成员:vector<string> v1;那么v1是放在栈上的吗?如果v1很大,那是不是成为程序潜在的问题了?

支持(0) 反对(0) Patrickz10 | 园豆:7 (初学一级) | 2015-03-05 17:19

@Patrickz10: 你最好多写点字把问题讲清楚,比如:

class A {

  private:

       vector<string> v1;
};

A a; // v1 分配在栈上,但是不保证 vector 或 string 的内部容器分配在栈上。

A * a = new A; // v1 分配在堆上,假定没重载 new 操作符。

A * a = (A *)malloc(sizeof(A)); // v1 分配在堆上。

支持(0) 反对(0) Launcher | 园豆:45040 (高人七级) | 2015-03-05 17:24

@Launcher: “不保证 vector 或 string 的内部容器分配在栈上”,你意思是不是说,虽然我没有用new申请内存空间,但是c++仍然可以会自主决定要将我的对象放到栈上还是堆上,是吗?

支持(0) 反对(0) Patrickz10 | 园豆:7 (初学一级) | 2015-03-05 17:36

@Patrickz10: 不是由 C++ 来自主决定的,一切取决于你的代码。比如:

class A {

  public :

      A(){ p = malloc(1000);}

 

   private:

      void * p;
};

 

A a; // a 分配在栈上,变量 p 也分配在栈上,但是 p 指向的内存空间分配在堆上。

支持(0) 反对(0) Launcher | 园豆:45040 (高人七级) | 2015-03-05 17:40

@Launcher: class A {
  private:
       vector<string> v1;
};

A *getA()
{
    A * a = new A; // 没重载 new 操作符的。
    return a;
}

int main()
{
    A *b = getA();
    //use b
    return 0;
}

那按上面代码,指针变量a和类成员v1应该在栈上,而a指向的对象在堆上,当我调用完getA函数之后,函数的栈空间会自动回收,那么获取的对象指针所指的对象的成员不就指向无意义的内存空间(已被回收的栈空间)了吗?

支持(0) 反对(0) Patrickz10 | 园豆:7 (初学一级) | 2015-03-05 17:56

@Patrickz10: 分配在栈上的是变量 a,而不是 a 指向的内存空间(new A),因此只要不调用 delete 操作,已分配的内存空间始终有效。

支持(0) 反对(0) Launcher | 园豆:45040 (高人七级) | 2015-03-05 18:04

@Patrickz10: 鉴于你的基础知识薄弱,你需要先找本 C 语言的书看下,把变量、指针的概念搞清楚了。

支持(0) 反对(0) Launcher | 园豆:45040 (高人七级) | 2015-03-05 18:05

@Launcher: 我就是在弄清这些概念才会有这些疑问啊,但我想你还没有理解我的问题,我是迷惑,上面代码v1应该在栈上,是不是?但是“a 指向的内存空间(new A)”却在堆上,这按书中的说法是确切无疑的,对不对?

支持(0) 反对(0) Patrickz10 | 园豆:7 (初学一级) | 2015-03-05 18:29

@Patrickz10: 但是问题却来了,当我调用完getA函数之后,函数的栈空间会自动回收,那么获取的对象指针(即b)所指的对象的成员v1就指向无意义的内存空间(已被回收的栈空间)了,这按书中的说法也是确切无疑的推论,是吧?

支持(0) 反对(0) Patrickz10 | 园豆:7 (初学一级) | 2015-03-05 18:33

@Launcher: 可是实际不是,写一个测试程序就知道了,应该按这个内存分配思路,下面程序应该会报错,可是事实却能正常运行

#include <string>
#include <vector>
using namespace std;

class A {
public:
    A(void) :v1(10) {v1[0]="abc";}
    vector<string> v1;
};

A *getA()
{
    A * a = new A; // 没重载 new 操作符的。
    return a;
}

int main()
{
    A *b = getA();
    string v1("ABCDEFHIMDI");
    printf(b->v1[0].data());
    if(b->v1.size()==10) printf(":ok");

    return 0;
}

程序正常的输出abc:ok

支持(0) 反对(0) Patrickz10 | 园豆:7 (初学一级) | 2015-03-05 18:37

@Patrickz10: 以 32 程序描述:

A *getA()
{

    // a 变量分配在栈上,假设地址(&a)为 0x0036fd6c,此地址开始的连续 4 个字节不能再分配。

    // new A 分配在堆上,假设首地址为 0x00381438;

    // 将 0x00381438 赋给 a,也就是首地址为 0x0036fd6c 的连续 4 个字节存放的是 0x00381438 这个整数。
    A * a = new A; 

 

    // 将首地址为 0x0036fd6c 的连续 4个字节存放的数据(整数 0x00381438)写入 EAX 寄存器 .
    return a;   
}

// 函数调用完成,现在开始自动清理为执行此函数分配在栈上的空间,在我们这里,原先为存储 a 使用的首地址为 0x0036fd6c 的连续 4个字节可以被再次分配。

 

// b 变量分配在栈上,假设地址(&b)为 0x0036fe8c,此地址开始的连续 4 个字节不能再分配。

// 调用 getA

// 从 EAX 读取整数 0x00381438,将其赋值给 b,也就是首地址为 0x0036fe8c 的连续 4 个字节存放的是 0x00381438 这个整数。

A *b = getA();

从这个稍微简单点的描述中我们可以看到,函数的栈空间会自动回收,获取的对象指针并没有指向无意义的空间,为什么呢,因为我们并没有对分配在堆上的首地址为 0x00381438 的空间做任何回收操作。那么你在这里理解的错误之处,就是对指针的概念模糊,return a,返回的不是 a 这个变量,而是 &a(0x0036fd6c) 中存储的整数值 (0x00381438),a 分配在栈上,会自动回收,但是 a 中保存的值(0x00381438)是分配在堆上的内存的首地址,那么不会自动回收。

 

如果仅仅看你写的代码,可以修改成这样,也是正确的:

int getA()

{

      A * a =new A;

      return (int)a;
}

或者这样:

int getA()

{

      A * a =new A;

      int n = 0;
 memcpy(&n, &a, sizeof(A*));
 return n;

}

 

A *b = (A *)getA();

支持(0) 反对(0) Launcher | 园豆:45040 (高人七级) | 2015-03-06 10:02
0

C++:在堆上创建对象,还是在栈上? | DevBean's World
http://www.devbean.net/2014/02/cpp-create-object-on-heap-or-stack/

并不是说指针指向的对象都是在堆上创建的。下面的代码则使用指针指向一个在栈上创建的对象:

Object obj;
Object *pObj = &obj;

twlhxy02 | 园豆:204 (菜鸟二级) | 2016-06-01 14:42
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册