📄 怎样判断一个指针指向的对象是否已经被析构.htm
字号:
API来判断指针指向的内存是否可读写。当然,这种方法是不可行的。C++对象的内存是分配在栈或者堆中的。不管何种情况下,栈内存永远是可读写的。而在系统堆中分配的内存的一个特性就是无法直接干预内存分页的提交和回收。也既是说,用new操作符分配的内存,即使delete后,也无法预料该内存是否被系统回收,它仅仅被系统标记为未使用而已。所以IsBad*系列API是无法判断某指针指向的对象是否已经被析构。</P>
<P>有人又提出了使用使用C++的错误处理来处理异常。这也是不可行的。除非该对象包含虚函数,并且你调用了虚函数,才有可能发生异常。而在大多数情况下,使用一个被析构的对象并不会发生异常。但是你的代码会影响到程序中的其他的数据,最终导致程序的运行结果异常。</P>
<P>还有人提出了如下的方法:</P>
<P>class cfoo<BR>{<BR>public bool
isValid()<BR>{<BR> if (this !=
NULL)<BR> return
true;<BR> else
<BR> return
false;<BR>}<BR>}</P>
<P>这种方法显然是可笑的。此人估计是个新手(当然,我也是新手)。对象内部的this指针时由调用者决定的。调用者在调用成员函数前,会将当前对象的地址保存到ECX寄存器。成员函数内部便使用ECX寄存器中保存的值来作为this指针的值。下面的代码也许可以让你更深刻得理解:</P>
<P>#include <stdio.h></P>
<P>class ctest<BR>{<BR>public:<BR> void
PrintThisPtr();<BR>};</P>
<P>void
ctest::PrintThisPtr()<BR>{<BR> printf("0x%08X\n",this);<BR>}</P>
<P>int main()<BR>{<BR> ctest
*p=(ctest*)0x12345678;<BR> p->PrintThisPtr(); //It will
print
"0x12345678"<BR> /*------------------------<BR> 这句代码的汇编形式为:<BR> mov ecx,
12345678H<BR> call ctest::PrintThisPtr<BR> --------------------------*/<BR> return
0;<BR>}</P>
<P>最后又有人提出了一个方法:</P>
<P>class foo<BR>{<BR>public:<BR>
foo():m_Valid(TRUE){}<BR> ~foo(){m_Valid =
FALSE;}<BR> BOOL IsValid()<BR>
{<BR>
try<BR>
{<BR>
if(m_Valid)<BR>
return TRUE;<BR>
else<BR>
return FALSE;<BR>
}<BR>
catch(...)<BR>
{<BR> return
FALSE;<BR> }<BR> }</P>
<P>private:<BR> BOOL m_Valid;<BR>};</P>
<P>可惜,这个方法仍旧不可行。在一个对象被析构后,该对象原先占用的内存就被标识为未使用(堆和栈的做法是不一样的)。在程序运行一段时间后,原先的这块内存会被一些无法预见的数据所覆盖,m_Valid的值也无法保证其正确性。后来,提出该方法的人又改进了代码:给m_Valid赋一个特殊的值,比如0xEFEFEFEF。这仅仅是一个讨巧的办法,你也许可以在混乱编程大赛中使用该方法,但是你不能在正式的工程中使用这个方法。这个方法无法保证100%不出问题。一个最容易想到的反例就是:在这块内存中,又分配了一个新的foo对象,新对象的m_Valid成员和旧对象的重合。</P>
<P>经过前面的分析,能够得到一个结论:C++对象的指针指向的仅仅是块普通的内存区域,和C语言中的结构体没有什么区别。所以,要判断一个指针指向的对象是否已经被析构必须借助于不依赖对象的外部标记才能实现。一个经常使用的方法就是双重指针。直接指向对象的指针仅有一个,引用对象必须使用一个指向该指针的双重指针。当对象析构时,将那个直接指向对象的指针赋值为NULL。其他的双重指针就可以根据这个指针的值来判断对象是否被析构。</P>
<P>#include <stdio.h></P>
<P>class CTest{<BR>public:<BR> int m;<BR>};</P>
<P>int main()<BR>{<BR>CTest *handle=new CTest;<BR>CTest**
p1=&handle;<BR>CTest** p2=&handle;<BR>CTest**
p3=&handle;<BR>delete
handle;<BR>handle=NULL;<BR>if(NULL!=handle)<BR>{<BR>(*p1)->m=100;<BR>(*p2)->m=200;<BR>(*p3)->m=300;<BR>}else{<BR>printf("对象已经析构");<BR>}<BR>return
0;<BR>}</P>
<P>这个直接指向对象的指针在此处就起到了句柄的作用。还有一种方法:</P>
<P>#include <stdio.h></P>
<P>class CTest{<BR>public:<BR> CTest(bool
*pIsValid)<BR>
{<BR>
*pIsValid=true;<BR>
m_pIsValid=pIsValid;<BR> };<BR>
~CTest()<BR>
{<BR>
*m_pIsValid=false;<BR> };
<BR>private:<BR> bool *m_pIsValid;<BR>};</P>
<P>bool IsValid;</P>
<P>int main()<BR>{<BR> CTest *ptr=new
CTest(&IsValid);<BR> delete
ptr;<BR> <BR> if(IsValid)<BR> {<BR> printf("对象还存在");<BR> }else{<BR> printf("对象已经析构");<BR> }<BR> return
0;<BR>}</P>
<P>每创建一个对象,必须有一个IsValid对应。如果要动态创建多个对象的话,IsValid也需要动态创建,并且IsValid的生存时间必须大于它标示的对象。但是IsValid的生存时间也不能是永久的,这样就产生内存泄漏了。</P>
<P>不管采用什么方法,必须依靠外部标示来表示对象的生存期,而且要尊循以下原则:每个对象必须与唯一的外部标示对应;每个外部标示也对应唯一的对象;外部标示的生存时间必须大于对象的生存时间;当确定不再需要查询对象的生存期时,应当释放外部标示。如果情况复杂的话,最好实现句柄表来查询。</P>
<P>#include <stdio.h><BR>#define HT_LEN 256<BR>class
foo<BR>{<BR>public:<BR> foo(){};<BR> ~foo(){};<BR> void
print(){printf("%d\n",m);};<BR> int m;<BR>};</P>
<P>typedef struct<BR>{<BR> unsigned int handle;<BR> foo
*ptr;<BR>} HTABLE;</P>
<P>HTABLE g_HandleTable[HT_LEN];<BR>unsigned int g_MaxHandle;</P>
<P>foo* GetObject(unsigned int handle)<BR>{<BR> int
i;<BR> for(i=0;i<HT_LEN;i++)<BR> {<BR> if(handle==g_HandleTable[i].handle)<BR> return
g_HandleTable[i].ptr;<BR> }return NULL;<BR>}</P>
<P>bool DeleteObject(unsigned int handle)<BR>{<BR> int
i;<BR> for(i=0;i<HT_LEN;i++)<BR> {<BR> if(handle==g_HandleTable[i].handle)<BR> {<BR> delete
g_HandleTable[i].ptr;<BR> g_HandleTable[i].handle=0;<BR> g_HandleTable[i].ptr=NULL;<BR> return
true;<BR> }<BR> }return false;<BR>}</P>
<P>unsigned int CreateObject()<BR>{<BR> int
i;<BR> for(i=0;i<HT_LEN;i++)<BR> {<BR> if(0==g_HandleTable[i].handle)<BR> {<BR> g_HandleTable[i].ptr=new
foo;<BR> g_HandleTable[i].handle=++g_MaxHandle;<BR> return
g_HandleTable[i].handle;<BR> }<BR> }return
0;<BR>}</P>
<P>int main()<BR>{<BR> unsigned int
hfoo=CreateObject();<BR> foo
*p=GetObject(hfoo);<BR> if(NULL!=p)<BR> {<BR> p->m=100;<BR> p->print();<BR> printf("HANDLE
VALUE:%d\n",hfoo);<BR> }else{<BR> printf("对象不存在");<BR> }<BR> DeleteObject(hfoo);<BR> return
0;<BR>}</P>
<P>以上代码还比较简陋,还可以慢慢完善。</P></SPAN><BR>
<DIV
style="FONT-SIZE: 14px; LINE-HEIGHT: 25px"><STRONG>作者Blog:</STRONG><A
id=ArticleContent1_ArticleContent1_AuthorBlogLink
href="http://blog.csdn.net/tabris17/"
target=_blank>http://blog.csdn.net/tabris17/</A></DIV>
<DIV
style="FONT-SIZE: 14px; COLOR: #900; LINE-HEIGHT: 25px"><STRONG>相关文章</STRONG></DIV>
<TABLE id=ArticleContent1_ArticleContent1_RelatedArticles
style="BORDER-COLLAPSE: collapse" cellSpacing=0 border=0>
<TBODY>
<TR>
<TD><A
href="http://dev.csdn.net/article/66/article/66/66748.shtm">怎样判断一个指针指向的对象是否已经被析构</A>
</TD></TR>
<TR>
<TD><A
href="http://dev.csdn.net/article/66/article/66/66082.shtm">C++之研究——指针和引用</A>
</TD></TR>
<TR>
<TD><A
href="http://dev.csdn.net/article/66/article/62/62883.shtm">C++之研究——对象的实现(2)</A>
</TD></TR>
<TR>
<TD><A
href="http://dev.csdn.net/article/66/article/62/62794.shtm">C++之研究——对象的实现</A>
</TD></TR>
<TR>
<TD><A
href="http://dev.csdn.net/article/66/article/57/57789.shtm">把Alexa工具条改装成木马</A>
</TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><A name=#Comment></A>
<TABLE cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width="100%" align=center
bgColor=#006699 border=0>
<TBODY>
<TR bgColor=#006699>
<TD id=white align=middle width=556 bgColor=#006699><FONT
color=#ffffff>对该文的评论</FONT> </TD></TR></TBODY></TABLE>
<DIV align=right><A id=CommnetList1_CommnetList1_Morelink
href="http://comment.csdn.net/Comment.aspx?c=2&s=66748">【评论】</A>
<A id=CommnetList1_CommnetList1_Hyperlink1
href="javascript:window.close();">【关闭】</A> <A
href="mailto:webmaster@csdn.net">【报告bug】</A>
</DIV><BR></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></FORM><!-- 版权 -->
<HR align=center width=770 noShade SIZE=1>
<TABLE cellSpacing=0 cellPadding=0 width=500 align=center border=0>
<TBODY>
<TR>
<TD vAlign=bottom align=middle height=10><A
href="http://www.csdn.net/intro/intro.asp?id=2">网站简介</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=5">广告服务</A> - <A
href="http://www.csdn.net/map/map.shtm">网站地图</A> - <A
href="http://www.csdn.net/help/help.asp">帮助信息</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=2">联系方式</A> - <A
href="http://www.csdn.net/english">English</A> </TD>
<TD align=middle rowSpan=3><A
href="http://www.hd315.gov.cn/beian/view.asp?bianhao=010202001032100010"><IMG
height=48 src="怎样判断一个指针指向的对象是否已经被析构.files/biaoshi.gif" width=40
border=0></A></TD></TR>
<TR>
<TD vAlign=top align=middle>北京百联美达美数码科技有限公司 版权所有 京ICP证020026号</TD></TR>
<TR align=middle>
<TD vAlign=top><FONT face=Verdana>Copyright © CSDN.NET, Inc. All Rights
Reserved</FONT></TD></TR>
<TR>
<TD height=15></TD></TR></TBODY></TABLE><!-- /版权 -->
<SCRIPT>
document.write("<img src=http://count.csdn.net/count/pageview1.asp?columnid=4&itemid=11 border=0 width=0 height=0>");
</SCRIPT>
</BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -