摘自:http://www.sudu.cn/info/html/edu/C/20070103/236588.html
一、你需要一个函数将一个数组赋值为等差数列,并将会在函数的外部使用他。
不合理:
int *GetArray( int n ) { int *p = new int[n]; for ( int i = 0; i < n; i++ ) { p[i] = i; } return p; } |
合理:
void GetArray( int *p, int n ) { for ( int i = 0; i < n; i++ ) { p[i] = i; } } |
解析:
检查内存泄露的最佳办法,就是检查完全配对的申请和释放,在函数中申请而在外部释放,将导致代码的一致性变差,难以维护。而且,你写的函数不一定是你自己使用的,这样的函数别人会不知道该怎么适当的使用,如果他是个DLL的导出函数,并且你在不同的平台下使用了,便会导致系统崩溃。最佳的解决办法就是在函数调用的外面将内存申请好,函数只对数据进行复制。
二、你需要写一个类来为你管理一个指针,这个类将封装对指针的申请内存、释放和其他一些基本操作。
不合理:
class A { public: A( void ) {} ~A( void ) { delete []m_pPtr; } void Create( int n ){ m_pPtr = new int[n]; } private: int *m_pPtr; }; |
合理:
class A { public: A( void ) : m_pPtr(0){} ~A( void ) { Clear(); } bool Create( int n ){ if ( m_pPtr ) return false; m_pPtr = new int[n]; return ture; } void Clear( void ) { delete []m_pPtr; m_pPtr = 0; } private: int *m_pPtr; }; |
解析:
不合理的代码就在于当你重复调用Create的时候就会造成内存泄露,解决的办法就是在new之前判断一下指针是否为0。要能够有效的执行这个判断,则必须在构造的时候对指针进行初始化,并为这个类添加一个Clear函数来释放内存。
三、接上题的Create函数,你目前需要根据传入的参数做一些比较复杂的算法操作,并对申请的数组赋值。
不合理:
bool Create(int *a, int n ) { if ( m_pPtr ) return false; m_pPtr = new int[n]; for ( int i = 0; i < n; i++ ) { m_pPtr[i] = 3 / a[i]; } return true; } |
合理:
template class auto_array { public: explicit auto_array(_Ty *pPtr=0)throw():m_Ptr(pPtr){} ~auto_array(){delete[]m_Ptr;} void reset(_Ty *pPtr=0){if(pPtr!=m_Ptr){delete[]m_Ptr;m_Ptr=pPtr;}} _Ty* release(void){_Ty *pTemp=m_Ptr;m_Ptr=0;return pTemp;} private: auto_array(const auto_array&other){} auto_array& operator=(const auto_array& other){} _Ty *m_Ptr; }; bool A::Create(int *a, int n ) { if ( m_pPtr ) return false; auto_array PtrGuard( new int[n] ); for ( int i = 0; i < n; i++ ) { if ( 0 == a[i] ) { return false; } PtrGuard .get()[i] = 3 / a[i]; } m_pPtr = PtrGuard.release(); return true; } |
解析:
在循环中,当参数数组a中的某一个值为0时,将会产生除0异常,那么,这将会导致你在上面为m_pPtr申请的内存不能合理的释放。为了解决这个问题,我们写了一个auto_array作为卫兵来看守企图逃逸的指针。在auto_array对象PtrGuard析构的时候他会同时删除附加在他身上的内存指针。我们首先用PtrGuard来进行所有的指针操作,在确定操作完全结束的最后,把指针再赋给真正的变量,并使PtrGuard放弃对该指针的附加,这样我们就得到了一个最安全的结果。另外需要注意的是,C++的STL库里本来有一个和auto_array功能非常相似的模版类auto_ptr,不过他只支持单个对象的内存,不支持数组,写这样一个auto_array也是不得已而为之。
四、你需要开辟一段内存来存放和管理一个4 x 4的矩阵,并单位化之。
不合理:
int aMatrix[4][4]; for ( int i = 0; i < 4; i++ ) { for ( int j = 0; j < 4; j++ ) { if ( i == j ) { aMatrix[i][j] = 1; } else { aMatrix [i][j] = 0; } } } |
合理:
int aMatrix[4 * 4]; for ( int i = 0; i < 4; i++ ) { for ( int j = 0; j < 4; j++ ) { if ( i == j ) { aMatrix[ i * 4 + j ] = 1; } else { aMatrix [ i * 4 + j ] = 0; } } } |
解析:
在所有时候都要避免使用多维数组,数组维数的增加,相应的程式复杂度将会以几何级数的方式增加,也更加的难于理解。
五、你需要对上面那个矩阵赋值,使他从左上角向右下角按先纵后横的顺序给他赋值
不合理:
for( int i = 0; i < 4; i++ ) { for ( int j = 0; j < 4; j++ ) { aMatrix[ j * 4 + i ] = i * 4 + j; } } |
合理:
for( int i = 0; i < 4; i++ ) { for ( int j = 0; j < 4; j++ ) { aMatrix[i * 4 + j ] = j * 4 + i; } } |
解析:
尽量确保顺序的访问数组的每一个元素。由于视窗系统内存的管理模式,内存是分页管理的。顺序访问数组能基本确保页面不会来回转换,从而减少了页失效的数量,提高了程式的整体性能。这种性能的提升对于大的数组尤为明显。
六、你需要用3个float值来表示一个三维的点,并要写一个函数对一个三维点的数组进行计算赋值。
不合理:
void foo( float *pPoints[3] ) { float aPoint[3] = { 1.0f, 2.0f, 3.0f }; int nCount = (int)_msize( pPoints ); for ( int i = 0; i < nCount; i++ ) { pPoints[i][0] = aPoint[0]; pPoints[i][1] = aPoint[1]; pPoints[i][2] = aPoint[2]; } } |
合理:
struct POINT3 { float x, y, z; }; void foo( POINT3 *pPoints, int nCount ) { POINT3 Pt = { 1.0f, 2.0f, 3.0f }; for ( int i = 0; i < nCount; i++ ) { pPoints[i] = Pt; } } |
解析:
有两点,一,不要使用_msize对数组的大小进行测定,_msize只能对使用malloc或calloc申请的内存进行大小测定,对于其他的如new或一些API,将会导致程式的崩溃。在设计此类需要传入数组的函数时,别忘了把数组的元素数量也做为参数一并传入,哪怕他是固定的,这将是个良好的习惯。二,对于float[3]这种类型,尽量避免直接使用他,最佳的办法就是用struct对其进行简单的封装,在复制的时候直接使用“=”就能进行准确的按位赋值了。
七、你有一个函数的定义,在这个函数中会new一个比较大的对象Data,并在计算后将他删除。但这个函数将被频繁调用。
不合理:
void foo( void ) { Data *p = new Data; CalcData( p ); delete p; } |
合理:
char Buf[sizeof(DATA)]; void foo( void ) { Data *p = new(Buf) Data; CalcData( p ); } |
解析:
new(buf) type;是定位的new语法,他不会真正的分配内存,而是简单的在指定的已分配的内存起点上划分出一段和类型大小匹配的空间,并直接在这段内存上对该类型进行构造对象,并返回对象的指针。由于他没有真正的分配内存空间,因此他的效率是非常高的,在类似于上述例程中,频繁申请和释放一个大对象的操作,定位的new能带来非常大的效率提升。