C++与堆栈的讨论

JoeRen 2003-09-13 03:15:00
1、栈中的多态
一般而言,C++的多态对象必须在堆中创建。若要在栈中创建,则派生类要定义扩展的数据成员就有些困难。这是由于任何C++对象必须具备一个静态的sizeof值以便管理堆栈框架。
但是,只要不在派生类中定义数据成员,要在栈中实现多态是可行的。关键在于sizeof算符和标准的C库函数memcpy。由于派生类没有定义扩展的数据成员,所以派生类和基类的唯一区别是派生类拥有不同的VTABLE指针。而这个VTABLE指针根据标准是应当包括在sizeof里面的。这样,我们可以对对象进行memcpy操作,将所有数据(包括VTABLE)替换成另一个对象的,即可修改对象的实际类型。

2、在栈中动态分配
堆栈一般是静态分配的,但它也具备动态的潜力。这是因为堆栈是可扩展的,尽管所做的扩展在退出当前函数的时候就消失了。
两种流行的编译器,VC++和G++分别提供了两种不同的堆栈动态分配方案。
VC++提供了库函数_alloc,用法跟标准库函数malloc一样,但它在栈中分配空间,所分配空间退出函数时自动释放,因此没有相对应的_free函数。
G++提供了“动态分配数组”的支持。例如:
void foo(int size)
{
char array[size];
...
}
可以在G++中利用动态分配数组定义出一个宏模拟_alloc的功能。但反过来就不好模拟了,因为这是语言上的扩充,而不仅仅是库函数的扩充。
...全文
60 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
TianGuangZao 2003-09-15
  • 打赏
  • 举报
回复
听人家说,c 标准里没有堆和栈的说法,只有动态存储和静态存储的区别,堆栈是依赖具体机器的,有些机器根本没栈,靠多个寄存器工作。
不过我们绝大多数都用 intel pc,都默认了。
JoeRen 2003-09-14
  • 打赏
  • 举报
回复
我又进行了一些测试。现在我可以将动态数组和栈中动态分配的原理解释如下:
看以下程序段(Jinhao(辣子鸡丁)提供的):
char *a1,*a2,*a3;
void foo(int size)
{
char array[size];
array[0]='1';
array[size-1]='\0';
a1=array;
char *a=new char[5];
a[0]='2';
a[4]='\0';
a2=a;
}
该函数中有array和a两个临时变量。它们必须在堆栈中有一个独一无二的地址。而array实际上是一个char *,指向堆栈中的一块内存。编译器生成的代码中,将通过动态调整堆栈指针寄存器的值来保证这块内存不被以后的函数调用改写。一种可能的堆栈布局将如下:
参数size
返回地址
指针array
指针a
array的内容...(共size个字节)
堆栈指针指向这里 -> (array的最后一个字节,可能经过对齐)

我们知道,当函数返回的时候,堆栈指针将退回到调用前的状态,也就是指向第一个参数前的位置。因此当下次再调用cout<<a1的时候,尽管a1指向已被释放的堆栈内容,但它依然有效。我们试作出它的内存布局:
参数a1 (原参数size)
参数cout (原返回地址)
返回地址 (原指针array)
堆栈指针指向这里 -> (原指针a)
(array的内容...)

因此,动态array的内容没受到破坏,就一点不奇怪了。当然,编程序绝不能依靠这种危险的行为。

相反地,另一个函数
void fun()
{
char a[3]={'3','4','\0'};
a3=a;
}
就没那么幸运了。它的内存布局为
返回地址
a的内容
堆栈指针指向这里 -> (a的最后一个字节,可能经过对齐)

在调用cout<<a3的时候,
参数a1 (原返回地址)
参数cout (a的内容)
返回地址
堆栈指针指向这里 ->
很明显,a的内容被破坏了。
JoeRen 2003-09-14
  • 打赏
  • 举报
回复
另外,我的主贴上有一个错误,特此更正。
VC中提供的堆栈动态分配函数名称应当是_alloca,而不是文中的_alloc。
JoeRen 2003-09-14
  • 打赏
  • 举报
回复
To Jinhao(辣子鸡丁):
你的例子不能证明动态数组不是在栈上分配,只能说明在退出函数之后该内存没被修改而已。

我下面的例子可以证明动态数组确实是在栈上分配的。(同样在Dev-C++上编译)
void foo(int size)
{
char NormalArray[10];
char DynamicArray[size];
printf("Address of Normal Array=%p,DynamicArray=%p\n",NormalArray,DynamicArray);
}

int main(int argc, char *argv[])
{
foo(20);
system("pause");
}

输出为:
Address of Normal Array=0022FF30,DynamicArray=0022FF00

很明显,动态数组跟普通数组占据的内存空间很接近,除了在堆栈分配之外,别无其他解释。

另外,placement new当然有它的用处,但对本问题无帮助。
试考虑如下工厂函数:
SomeObject Create(args...);
如果该函数要动态返回不同的SomeObject派生类,该怎么办呢?难题在于函数返回的不是指针,不是引用,因此要调用SomeObject的复制构造函数。但这样一来,返回的对象拥有的VTABLE将是基类的,而不是派生类的。
解决的方法是在基类的复制构造函数中使用memcpy拷贝VTABLE。使用placement new有困难,因为它又隐含了一个构造函数的调用,搞不好就会无限递归。
另外,通过首先备份数据的方法,复制构造函数也可以实现深拷贝。
Jinhao 2003-09-14
  • 打赏
  • 举报
回复
哦,不是看错了哈
JoeRen 2003-09-14
  • 打赏
  • 举报
回复
退出的时候修改esp?不是吧?标准的进入/退出代码如下:
push ebp
mov ebp,esp
sub esp,(局部变量所占空间大小)
...
mov esp,ebp; //是不是说这个?
pop ebp;
ret
Jinhao 2003-09-14
  • 打赏
  • 举报
回复
恩,厉害,在foo退出时的确修改了esp
loveghb 2003-09-14
  • 打赏
  • 举报
回复
to: JoeRen (地球发动机)
厉害的!
学习!!!
panzhaoping 2003-09-14
  • 打赏
  • 举报
回复
大哥,你还使好好学学C++,功底不够
你知道在哪里是堆中分配内存,哪里是栈中分配内存
劝你还是好好看看<<升入探索C++对象模型>>这本书。
Jinhao 2003-09-13
  • 打赏
  • 举报
回复
解大便时想到的!!
第一个问题为什么不用placement new来创建一个对象,而用无性生殖来克隆一个对象???
Jinhao 2003-09-13
  • 打赏
  • 举报
回复
1,理解了,但没试过,也不知道为什么要这样做

2,既然是栈,那么它的大小就应该不会改变!在C++中数组的下标参数必须是个const
而楼主所说的,G++提供了“动态分配”其实是在堆上分配的内存。我可以用下面的代码来证明G++中的“动态分配”其实是在堆上分配的。
编译环境Dev-C++

char *a1,*a2,*a3;
void foo(int size)
{
char array[size];
array[0]='1';
array[size-1]='\0';
a1=array;
char *a=new char[5];
a[0]='2';
a[4]='\0';
a2=a;
}

void fun()
{
char a[3]={'3','4','\0'};
a3=a;
}

int main()
{
int i=2; //i大于等于2
foo(i);
cout<<a1<<a2; //如果是在栈上分配的话,就不可能是12,因为a1正如楼主所说的在栈里。
fun();
cout<<a3<<endl;//正如楼主所说,a3的指向的对象其实在fun()结束时就被销毁了
}

只不过在看到这篇帖子之前,确实不知道G++中可以用这种方法来分配内存。
fishsward 2003-09-13
  • 打赏
  • 举报
回复
楼主说"在栈中动态分配"是笔误,还是其他意思?????????????
你的例子和解释中我怎么没有看到"动态分配"?????


"栈中的多态"??? 你理解的"栈"是怎样的? "多态"是怎样理解的???
好像跟我以前对"栈"和"多态"的理解不一样啊?

赐教!!!!!!
csdn5211 2003-09-13
  • 打赏
  • 举报
回复
To: steedhorse(晨星)

呵呵,我还以为我水平太差劲才看不懂呢!
晨星 2003-09-13
  • 打赏
  • 举报
回复
虽然楼主写的在下未能完全看懂,但感觉还是有道理的呀。
楼上两位怎么否定的那么彻底?
zhangwendi 2003-09-13
  • 打赏
  • 举报
回复
请先把堆和栈搞清,动态内存分配与常规内存分配搞清楚。
cnswdevnet 2003-09-13
  • 打赏
  • 举报
回复
faint. 楼主还是再好好学学C++再来发表高论吧.

64,282

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧