无用单元和悬挂引用
Posted 郑哲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了无用单元和悬挂引用相关的知识,希望对你有一定的参考价值。
1.无用单元:
指一块存储区(或资源),该存储区虽然是程序(或进程)的一部分,但是在程序中却不可再对其引用。
按照C++的规定,我们可以说,无用单元是程序中没有指针指向的某些资源;
例如:
1 void main() 2 { 3 char *p = new char[1000];//分配一个包含1000个字符的动态数组 4 char *q = new char[1000];//另一个动态内存 5 p = q;//将p和q进行一些操作的代码 6 /*p所指向的1000个字符的存储区会发生什么?此时,p和q指向相同的区域,没有指针指向之前p指向 7 的旧存储区!该存储区还在,仍然占用着空间。但是程序已经不能访问该区域*/ 8 }
2.悬挂引用:
当两个指针同时指向一个地址时,通过一个指针删除了该地址的数据,另一个指针就产生了悬挂引用。
1 void main() 2 { 3 char *p; 4 char *q; 5 p = new char[1024]; 6 q = p; 7 delete[]p; 8 p = 0;//现在q就是一个悬挂引用 9 }
无用单元控制再软件设计中时一个非常重要的问题,因为它会影响应用的整体性能。
即使是不太重要的应用程序,再持续运行一段时间未停止,内存泄露也会导致严重的问题。
随着越来越多的内存变成无用单元,应用程序(和整个系统)的性能逐渐降低,导致越来越多的虚拟内存分页活动。最终,由于分页文件被占满,
整个系统只能停止。
并非所有程序都可以随意暂停,如核电站控制程序,一旦暂停可能将导致灾难性的后果。
C++在何时产生无用单元:
1.从函数退出时,在函数内部创建的所有局部变量(包括对象)以及按值传递的所有参数都不可访问。
2.从块退出时,在块内部声明的所有局部变量(包括对象)都不可访问。
3.任何复杂表达式包含的临时变量,在不需要时必须全部予以销毁,否则它们将成为无用单元。
4.任何动态分配的对象,在不需要时必须由程序员显式地销毁。
1 TPerson::TPerson(const char _birthData[]) 2 :birthData(_birthData),address(0) 3 {} 4 5 char *strdup(const char* src) //辅助函数 6 { 7 char *ptr = new char[strlen(src) + 1]; 8 strcpy(ptr, src); 9 return ptr; 10 } 11 12 TPerson::TPerson(const char _theName[], const char _theAddress[], unsigned long _theSSN, const char _theBirthDate[]) 13 :ssn(theSSN), birthDate(_theBirthDate) 14 { 15 name = (_theName ? strdup(theName) : 0); 16 address = (_theAddress ? strdup(_theAddress) : 0); 17 }
辅助函数为数据成员申请一个新的内存空间。
无用单元的收集:
不仅只是析构函数来回收内存,一般函数也要回收内存。
1.析构函数
1 TPerson::~TPerson() 2 { 3 delete[]name; 4 delete[]address; 5 }
2.成员函数
1 void TPerson::SetName(const char newName[]) 2 { 3 unsigned oldLength = name ? strlen(name) : 0; 4 unsigned newLength = newName ? strlen(newName) : 0; 5 if (oldLength < newLength) 6 { 7 delete[]name; //无用单元收集 8 name = (newName ? strdup(newName) : 0); 9 } 10 else 11 { 12 if (newName) strcpy(name, newName); 13 else { delete[]name; name = 0; } 14 } 15 }
内嵌对象,在主对象即将被销毁时,调用内嵌对象的析构函数,所有可以防止内存泄露。
以上是关于无用单元和悬挂引用的主要内容,如果未能解决你的问题,请参考以下文章
Max 函数的 6 种操作方法:API设计悬挂引用以及C++20 constraints