第一次提问,不能小气(100分):有关临时对象(生存期)的问题,有些疑惑。

ilovedudu 2005-11-26 05:29:19
这段代码是摘自《精通MFC》第1章的一个动态调用的例子,做了改动,
#include <iostream>
#include <conio.h>
using namespace std;

class V1
{
private:
int Size;
public:
V1()
{
Size=0;
cout<<"Construct a object."<<endl;
}
~V1()
{
cout<<"Destroy a object !"<<endl;
}
void DoShow()
{
cout<<"In member function DoShow()."<<endl;
}
};

void main()
{
int nObjSize=sizeof(V1);
void *pV1Obj=new char[nObjSize]; // 动态分配V1大小的内存块.
    v1();
*(int *)pV1Obj=*(int*)&V1(); // 记作(1):生成临时对象,结果显示马上被析构了
*(V1*)pV1Obj=V1();       // 既然被析构了,pV1Obj指向的地方有效吗???

((V1*)pV1Obj)->DoShow();

((V1*)pV1Obj)->~V1();
delete [] pV1Obj;
pV1Obj=NULL;
_getch();
}

运行结果...
Construct a object.
Destroy a object!
Construct a object.
Destroy a object!
Construct a object.
Destroy a object!
In member function DoShow().
Destroy a object!

临时对象构造之后,马上又调用了析调函数,那么这个临时对象占用的内存不就释放了吗?指针pV1Obj指向那个地方还可靠(有效)吗?每次生成的临时对象在同一个地址吗?对这个临时对象疑问多多,希望各位兄弟姐妹借题发挥,解我疑惑。先谢。
...全文
502 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
ilovedudu 2005-11-29
  • 打赏
  • 举报
回复
本来只想给1分的,发完之后发现还剩几分,都给你了,知足吧:-)
fiftymetre 2005-11-29
  • 打赏
  • 举报
回复
才给我5分啊..白占了个第一
ilovedudu 2005-11-29
  • 打赏
  • 举报
回复
谢谢各位了
junhao_666 2005-11-28
  • 打赏
  • 举报
回复
学习
ilovedudu 2005-11-28
  • 打赏
  • 举报
回复
更正上面一句:在DoShow()函数中加入输出size2的语句
ilovedudu 2005-11-28
  • 打赏
  • 举报
回复
对这个问题做些小实验挺有意思,但是不知道编译器是怎么做的,稀里糊涂。
原来是用动态分配一个V1大小的存块,现在不用动态分配,而是这样做也可以(好灵活的)
int obj;   //4字节大小,也可以用char obj[4]。
((V1*)&obj)->DoShow();
竟然可以这样调用V1类的公有函数,显示是个不确定的数,可能因为没有初始化成员数据。
于是用 ((V1*)&obj)->size=8; //访问或修改公有成员数据(必须公有的才行,访问成员函数也一样)
再使用 ((V1*)&obj)->DoShow(); 会显示8。
----------
下面这种情况更有趣,请看
我在V1类中又加入一个公有成员数据int size2,此时V1类有两有整型成员数据,sizeof(V1)为8;并在DoShow()函数中加输入size2的语句,用于观察值。
之后我在main()中这样做;
char obj[5]; //小于8字节,如果是改为char obj[4]就会出现异常。
((V1*)obj)->size=1;
((V1*)obj)->size2=2;
((V1*)obj)->DoShow();
会显示1和2。
这里就很奇怪了,obj大小只有5字节,V1类对象的大小是8字节,竟然没有出现错误结果!!!汗
Mybox 2005-11-28
  • 打赏
  • 举报
回复
呵呵,这么多人,来晚了:)
manplus 2005-11-28
  • 打赏
  • 举报
回复
mark
ilovedudu 2005-11-27
  • 打赏
  • 举报
回复
恩,谢谢楼上给的分析。明白意思了。
edrftgyh 2005-11-27
  • 打赏
  • 举报
回复
我另外补充一下,这个例子也仅限与对V1这样简单的例子适用,因为我们看到pV1Obj 对象始终没有调用到构造函数, 而有时候编译器会在我们的构造函数里插入很多很多代码, 这样的话我们就有必要在pV1Obj所指向的内存区域调用一次V1的构造函数,像这样: new (pV1Obj) V1();
edrftgyh 2005-11-27
  • 打赏
  • 举报
回复
楼上正解, 说的很透彻了.
Zark 2005-11-27
  • 打赏
  • 举报
回复
以下是我的解释:

int nObjSize=sizeof(V1);
void *pV1Obj=new char[nObjSize]; // 动态分配V1大小的内存块.
    v1(); //-这里大约是个笔误,应为V1()

---> 在这里产生了一个V1类的临时对象,因此存在构造和析构各一次.


Construct a object. -->
Destroy a object! -->这两个
Construct a object.
Destroy a object!
Construct a object.
Destroy a object!
In member function DoShow().
Destroy a object!


*(int *)pV1Obj=*(int*)&V1(); // 记作(1):生成临时对象,结果显示马上被析构了

---> 在这里再次产生了一个V1类的临时对象,因此存在构造和析构各一次.并且将这个临时对象的值(状态)拷贝到了pV1Obj所指向的堆内存区,由于字节型拷贝,所以不存在构造函数的调用,但实际上类似于在pV1Obj指向的地区"生成"了一个V1类的对象,所以pV1Objt指向一个"正常"的V1类对象



Construct a object.
Destroy a object!
Construct a object. -->
Destroy a object! -->这两个
Construct a object.
Destroy a object!
In member function DoShow().
Destroy a object!

*(V1*)pV1Obj=V1();       // 既然被析构了,pV1Obj指向的地方有效吗???


---> 在这里又产生了一个V1类的临时对象,因此存在构造和析构各一次.又将这个临时对象的值(状态)拷贝到了pV1Obj所指向的堆内存区,同样由于字节型拷贝,所以不存在构造函数的调用,由于这个临时对象和上一样的值(状态)是一样的,所以看上去是一样的,没有变.因此在这里pV1Obj仍指向一个"合法"的变量.



Construct a object.
Destroy a object!
Construct a object.
Destroy a object!
Construct a object. -->
Destroy a object! -->这两个
In member function DoShow().
Destroy a object!

((V1*)pV1Obj)->DoShow();

---> 调用V1类的成员函数DoShow(),这时pV1Objt仍指向一个"合法"的V1类对象.



Construct a object.
Destroy a object!
Construct a object.
Destroy a object!
Construct a object.
Destroy a object!
In member function DoShow(). -->这个
Destroy a object!

((V1*)pV1Obj)->~V1();
---> 显式调用V1类的析构函数,完毕后pV1Objt仍指向一个"合法"的V1类对象.



Construct a object.
Destroy a object!
Construct a object.
Destroy a object!
Construct a object.
Destroy a object!
In member function DoShow().
Destroy a object! -->这个

delete [] pV1Obj;

--> 至此,那个"合法"的V1类对象不再存在,但由于仅是"释放(delete)",所以在内存中pV1Objt指向地区内存仍存在,即使在此之后继续使用pV1Obj,也是可能的,但系统随时都可以改写这部份内存,因为你已申请"释放(delete)".
pV1Obj=NULL;
_getch();

little_duck 2005-11-27
  • 打赏
  • 举报
回复
要给我分哦
点 管理 就可以了
空心兜兜 2005-11-27
  • 打赏
  • 举报
回复
顶!
我是大一新生
唉!
我离这些也不远了
xlsue 2005-11-27
  • 打赏
  • 举报
回复
to 回复人: braveconf() pV1Obj为无效的 .不知道什么意思?
ilovedudu 2005-11-27
  • 打赏
  • 举报
回复
不好意思问一下,因为是第一次发贴,所以还不知道怎么结贴。
楼下的兄弟指导一下。明天结贴,都有机会:-)
oosky2004 2005-11-26
  • 打赏
  • 举报
回复
Mark
学习。。
wzjall 2005-11-26
  • 打赏
  • 举报
回复
1:临时对象的确释放了
2:pV1Obj为无效的
3:临时对象不一定在同一地址,这取决操作系统
支持.

借别人的话:(看了后,应该能清楚了.)http://community.csdn.net/Expert/topic/4419/4419933.xml?temp=4.954165E-02

delete后表示,这块内存可以被用作别处了。但是在你覆盖之前 ,他的内容还是原来的。
obj delete后 如果不赋为 NULL 就成了“臭名昭著”的野指针了!
现在没有错,以后不一定什么时候就会带来灾难性的后果!
因此,在delete后 赋为NULL是一个好习惯!

所谓的释放,是指将申请的空间归还给管理函数
也就是说,这块空间以后可以分配给其他申请
比如你以后再new可能会将这块空间返回给你
而对块空间并没有做什么特别的操作
更不可能“删除”空间
但obj变量仍指向这个空间,
但是这个空间理论上已经不“属于”obj
如果继续用obj使用这个空间,可能不会有问题(当这块空间还未分配)
也可能会导致程序出乱
(如果这块空间已经再分配,将会有两个变量共用空间,造成混乱)

说得有点乱了
打个比方吧
我这边有一些篮球场地
我记录下正在使用的场地
有人要打球就来向我申请
我找到一个空场地然后告诉你号码,你就可以去那里打球
你打玩后回来告诉我,你不再使用这块场地了
我就做个标记,这个场地现在空闲
但是你仍然知道场地号,那个场地也仍然在那里
你仍可以去那里打球,我没有任何办法阻止你这么做
但是如果你坚持要在那里打的话,而后面又有人申请
并且我将这块“空地”使用权交给他
那就会有两组人同时在一个场地上打球
后果就是。。。

ilovedudu 2005-11-26
  • 打赏
  • 举报
回复
class V1
{
public:   //改为public
int size;
public:
V1(){.....};
~V1(){....};//与原来一样
void DoShow()
{
cout<<size<<endl;
}
};

main()函数中在*(int *)pV1Obj=*(int*)&V1(); *(V1*)pV1Obj=V1(); 
之后加入((V1*)pV1Obj)->size=2;
执行((V1*)pV1Obj)->DoShow()结果为2,说明pV1Obj指向的那个对象还存在,这样说不知对不对?
HelloIvan2005 2005-11-26
  • 打赏
  • 举报
回复
最好先学习好C++,然后再学习MFC.我建议走这样的路线:

<<C++primer>>, <<thinking in C++>>, 《深入浅出MFC》,<<inside VC++.net>>
加载更多回复(5)

64,683

社区成员

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

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