养成良好的C++编程习惯之内存管理的应用详解
开篇导读
虽然本系列文章定位为科普读物,但本座相信它们不但适合新手们学习借鉴,同时也能引发老鸟们的反思与共鸣。欢迎大家提出宝贵的意见和反馈^_^
在开篇讲述本章主要内容之前,本座首先用小小篇幅论述一下一种良好的工作习惯——积累、提炼与求精。在工作和学习的过程中,不断把学到的知识通过有效的方式积累起来,形成自己的知识库,随着知识量的扩大,就会得到从量变到质变的提升。另外还要不断地对知识进行提炼,随着自己知识面的扩大以及水平的提升,你肯定会发现原有知识库存在着一些片面、局限、笨拙甚至错误。这时,就需要你有精益求精精的态度和毅力对知识库进行优化整理。
也许以上这些各位都曾想过去实施,也明白其中的道理,但是自己就是给自己各种堂而皇之的借口不花时间去做。这样说吧,技术之路不好走,这个行业有两项基本要求:1、对软件开发工作本身有很大兴趣;2、耐得住寂寞。两者缺一不可,否则还是趁年轻早点转行吧,要不转做软件行业的销售、产品或者管理也行,总之就不要做开发^_^
--------------------------------------------------------------------------------
内存管理相关问题
一提起C/C++的内存管理,大部分人脑海里都立刻涌出new/delete/malloc/free等几个恐怖的单词吧?的确,C/C++的手工内存管理是它们区别于其他语言的一大特点,也像一道屏障立在那些想从其它语言转向C/C++的人士身前。由此也引起各大论坛对“C++人气低落”和“是否应该引入垃圾回收机制”等相关话题的剧烈争论。本座一直无视这些争论,其实并非本座不关心C++的发展与命运,相反,本座十分关心。虽然从现在的眼光看来,无论是C++身上有多少硬伤,C++委员会的大爷们和C++编译器厂商的大佬们如何扯猫尾。毕竟最爱就是最爱,残缺美也是美,不解释。本座之所以不关心这些争论,原因是因为看透了,一门语言就像一种人生,是有生命周期的,没落只是快慢的问题,旧事物总会被新事物取代,这是客观规律不可避免。秦始皇最终不也是没找到长生不老的仙丹么?只要曾经发光发热过,在还有价值的时候能为大众所用就已经无憾了。本座在此还要申明一种态度:本座并不排斥任何语言,相反,本座对新语言的诞生非常感兴趣。会去了解它们的特点,看看它们能帮助解决哪方面的问题。正如这几年,由于工作需要,本座用得最多的是Java和一些动态语言(它们的确能解决很多问题),而C/C++却没再用了。
嗯,扯远了,我们还是回到正题吧。说起C/C++的内存管理似乎令人望而生畏,满屏的new/delete/malloc/free,OutPut窗口无尽的MemoryLeak警告,程序诡异的0X00000004指针异常,仿佛回到那一年我们一起哭过的日子,你Hold得住吗?其实,现实并没有你想的那么糟糕。只要你付出一点点,花一点点心思,没错!就一点点而已——用C++类封装内存访问,就会解决你大部分的烦恼,让你受益终身。以Windows程序为例,主要有以下几种内存管理方式:
•虚拟内存(VirtualMemory)
•默认堆和私有堆(ProcessHeap&PrivateHeap)
•内存映射文件(FileMapping)
•进程堆栈(Heap,其实就是用malloc()或默认的new操作符在ProcessHeap里一小块一小块地割肉^_^)
•栈(Stack,内存由调用者或被调用者自动管理)
今天我们的主题是封装,至于每种内存模型的概念和API的使用方式在这里就不讲了,Google一下就知道。其实用C++封装上述前4种内存访问的原理都差不多,就是在构造函数或其他操作函数中分配内存,然后再在析构函数中确保内存被正确释放。虚拟内存、默认堆和私有堆的操作方式相似,这里就不一一展示了,有兴趣的朋友可以参考本座前几天发表的那篇无人问津的文章:《C++封装私有堆(PrivateHeap)》,哎!下面对内存映射文件的封装也只稍作介绍、我们主要讨论的是使用频率最高的malloc()和new的封装。
--------------------------------------------------------------------------------
内存映射文件
下面的代码把FileMapping句柄以及从FileMapping映射出来的内存分别封装到CFileMapping和CShareMemory中,可以直接使用CShareMemory可以创建一个FileMapping以及映射FileMapping的内存。
classCFileMapping
{
public:
CFileMapping(
LPCTSTRlpszName,
DWORDdwMaximumSizeLow,
DWORDdwMaximumSizeHigh =0,
HANDLEhFile =INVALID_HANDLE_VALUE,
DWORDflProtect =PAGE_READWRITE,
LPSECURITY_ATTRIBUTESlpFileMappingAttributes =NULL
)
{
m_hMap =::CreateFileMapping (
hFile,
lpFileMappingAttributes,
flProtect,
dwMaximumSizeHigh,
dwMaximumSizeLow,
lpszName
);
ASSERT(IsValid());
}
~CFileMapping()
{
if(IsValid())
VERIFY(::CloseHandle(m_hMap));
}
LPVOIDViewMap (
DWORDdwNumberOfBytesToMap,
DWORDdwFileOffsetLow,
DWORDdwFileOffsetHigh =0,
DWORDdwDesiredAccess =FILE_MAP_ALL_ACCESS
)
{
return::MapViewOfFile (
m_hMap,
dwDesiredAccess,
dwFileOffsetHigh,
dwFileOffsetLow,
dwNumberOfBytesToMap
);
}
BOOLUnViewMap(LPCVOIDlpBaseAddress)
{
return::UnmapViewOfFile(lpBaseAddress);
}
operatorHANDLE () {returnm_hMap;}
BOOLIsValid () {returnm_hMap!=NULL;}
private:
HANDLEm_hMap;
DECLARE_PRIVATE_COPY_CONSTRUCTOR(CFileMapping)
};
classCShareMemory
{
public:
CShareMemory(DWORDdwSize,LPCTSTRlpszName=NULL)
:m_fm(lpszName,dwSize)
{
ASSERT(dwSize>0);
}
~CShareMemory()
{
for(set<ULONG_PTR>::const_iteratorit=m_set.begin();it!=m_set.end();++it)
{
LPVOIDpV=(LPVOID)*it;
ASSERT(pV);
m_fm.UnViewMap(pV);
}
m_set.clear();
}
LPVOIDAlloc(DWORDdwNumberOfBytesToMap,DWORDdwFileOffsetLow)
{
LPVOIDpV=m_fm.ViewMap(dwNumberOfBytesToMap,dwFileOffsetLow);
if(pV)m_set.insert((ULONG_PTR)pV);
ASSERT(pV);
returnpV;
}
BOOLFree(LPCVOIDlpBaseAddress)
{
ASSERT(lpBaseAddress);
set<ULONG_PTR>::iteratorit=m_set.find((ULONG_PTR)lpBaseAddress);
if(it!=m_set.end())
m_set.erase(it);
returnm_fm.UnViewMap(lpBaseAddress);
}
private:
CFileMapping m_fm;
set<ULONG_PTR> m_set;
DECLARE_PRIVATE_COPY_CONSTRUCTOR(CShareMemory)
};
细心的朋友一定会发觉其实这样封装是有缺点的:首先,CShareMemory只能做内存共享,不能映射到真实文件(hFile永远为INVALID_HANDLE_VALUE);第二,可以对CShareMemory的Alloc()和Free()方法进一步封装,利用封装类的析构函数自动调用Free(),这样就可以完全消除“set<ULONG_PTR>m_set”这个属性了;第三,CFileMapping也可以把文件句柄一起封装进来,这样,从CreateFile()到CreateFileMapping()都受控了。这个不完美的封装就权当反面教材吧^_^
--------------------------------------------------------------------------------
malloc()系列函数
很多人都建议,在C++中尽量用new操作符取代malloc(),因为new类型安全,自动调用构造函数和析构函数等等。关于这点本座略有异议,在某些情形下malloc()其实比new更好使,效率方面我们可以不计较(几乎所有编译器的new操作符都用malloc()分配内存),从事过偏底层开发的人都清楚,我们避免不了处理rowdata(如:socket的收发缓冲区等)数据,这类数据是非常适合使用malloc()的,用new分配的内存还要停顿下来想想到底是用delete、delete[]、::delete、::delete[]中的哪个释放,malloc()分配的内存想都不用想,free()包打天下,何况人家有realloc()可以方便地重新调整内存,你有没有“renew”呢?总之一句话,malloc()的确是有存在的必要,就看接下来我们如何封装它了,请看代码:
//T :数据类型(内置类型或结构体)
//MAX_CACHE_SIZE :预申请内存的最大数目,以sizeof(T)为单位,如果该值设置合理,对于
// 需要动态递增缓冲区的buffer来说能大大提高效率
template<classT,size_tMAX_CACHE_SIZE=0>
classCBufferPtrT
{
public:
explicitCBufferPtrT(size_tsize=0,boolzero=false) {Reset();Malloc(size,zero);}
explicitCBufferPtrT(constT*pch,size_tsize) {Reset();Copy(pch,size);}
//拷贝构造函数要分两种情形
CBufferPtrT(constCBufferPtrT&other) {Reset();Copy(other);}
template<size_tS>CBufferPtrT(constCBufferPtrT<T,S>&other) {Reset();Copy(other);}
~CBufferPtrT(){Free();}
T*Malloc(size_tsize=1,boolzero=false)
{
Free();
returnAlloc(size,zero,false);
}
T*Realloc(size_tsize,boolzero=false)
{
returnAlloc(size,zero,true);
}
voidFree()
{
if(m_pch)
{
free(m_pch);
Reset();
}
}
template<size_tS>CBufferPtrT&Copy(constCBufferPtrT<T,S>&other)
{
if((void*)&other!=(void*)this)
Copy(other.Ptr(),other.Size());
return*this;
}
CBufferPtrT&Copy(constT*pch,size_tsize)
{
Malloc(size);
if(m_pch)
memcpy(m_pch,pch,size*sizeof(T));
return*this;
}
//动态扩大buffer
template<size_tS>CBufferPtrT&Cat(constCBufferPtrT<T,S>&other)
{
if((void*)&other!=(void*)this)
Cat(other.Ptr(),other.Size());
return*this;
}
//动态扩大buffer
CBufferPtrT&Cat(constT*pch,size_tsize=1)
{
size_tpre_size=m_size;
Realloc(m_size+size);
if(m_pch)
memcpy(m_pch+pre_size,pch,size*sizeof(T));
return*this;
}
template<size_tS>boolEqual(constCBufferPtrT<T,S>&other)const
{
if((void*)&other==(void*)this)
returntrue;
elseif(m_size!=other.Size())
returnfalse;
elseif(m_size==0)
returntrue;
else
return(memcmp(m_pch,other.Ptr(),m_size*sizeof(T))==0);
}
boolEqual(T*pch)const
{
if(m_pch==pch)
returntrue;
elseif(!m_pch||!pch)
returnfalse;
else
return(memcmp(m_pch,pch,m_size*sizeof(T))==0);
}
T* Ptr() {returnm_pch;}
constT* Ptr() const {returnm_pch;}
T& Get(inti) {return*(m_pch+i);}
constT& Get(inti) const {return*(m_pch+i);}
size_t Size() const {returnm_size;}
bool IsValid() const {returnm_pch!=0;}
//啊哈,竟然是类型安全的
operator T* () {returnPtr();}
operatorconst T* () const {returnPtr();}
//哇塞,竟然还支持索引访问
T&operator [] (inti) {returnGet(i);}
constT&operator [] (inti) const {returnGet(i);}
booloperator == (T*pv) const {returnEqual(pv);}
template<size_tS>booloperator == (constCBufferPtrT<T,S>&other) {returnEqual(other);}
//赋值操作符要分两种情形
CBufferPtrT&operator = (constCBufferPtrT&other) {returnCopy(other);}
template<size_tS>CBufferPtrT&operator = (constCBufferPtrT<T,S>&other) {returnCopy(other);}
private:
voidReset() {m_pch=0;m_size=0;m_capacity=0;}
size_tGetAllocSize(size_tsize) {returnmax(size,min(size*2,m_size+MAX_CACHE_SIZE));}
T*Alloc(size_tsize,boolzero=false,boolis_realloc=false)
{
if(size>=0&&size!=m_size)
{
size_trsize=GetAllocSize(size);
if(size>m_capacity||rsize<m_size)
{
m_pch=is_realloc ?
(T*)realloc(m_pch,rsize*sizeof(T)) :
(T*)malloc(rsize*sizeof(T)) ;
if(m_pch||rsize==0)
{
m_size =size;
m_capacity =rsize;
}
else
Reset();
}
else
m_size=size;
}
if(zero&&m_pch)
memset(m_pch,0,m_size*sizeof(T));
returnm_pch;
}
private:
T* m_pch;
size_t m_size;
size_t m_capacity;
};
//常用buffer类型的typedef
typedefCBufferPtrT<char> CCharBufferPtr;
typedefCBufferPtrT<wchar_t> CWCharBufferPtr;
typedefCBufferPtrT<unsignedchar> CByteBufferPtr;
typedefCByteBufferPtr CBufferPtr;
#ifdef_UNICODE
typedefCWCharBufferPtr CTCharBufferPtr;
#else
typedefCCharBufferPtr CTCharBufferPtr;
#endif
嗯。这里要解释一下为何需要两个拷贝构造函数和赋值操作符重载,首先,编译器为不同的模板参数生成不同的类,也就是说:CBufferPtrT<int,1>和CBufferPtrT<int,2>被看作是不同的类,另外,C++编译器为每个类提供了提供了拷贝构造函数和赋值操作符重载的默认实现(浅拷贝)。因此,上述的第一组拷贝构造函数和赋值操作符重载是改写编译器的默认实现,第二组拷贝构造函数和赋值操作符重载是处理其它类到本类的转换。
本座对这个封装灰常满意(唯一美中不足的就是cnblogs的编辑器太坑爹了,把代码弄乱^_^),它并非只是一个普通的malloc()封装,完全能可以把它看作是一种“支持索引访问的类型安全的动态缓冲区”。如果把它放在一个socket通信类中作为成员属性,充当跨越多个线程和多个方法访问的接收缓冲区和发送缓冲区的角色就最适合不过了(当然要自己做同步了)。大家可以调试一下下面的测试例子,了解一下它的用法:
测试用例
int_tmain(intargc,_TCHAR*argv[])
{
CBufferPtrbuffer;
unsignedcharc1 ="X";
unsignedcharpc1[]="123";
unsignedcharpc2[]="abc";
buffer.Cat(&c1);
buffer.Cat(pc1,3);
buffer.Cat(pc2,3);
CBufferPtrT<unsignedchar,10>buffer2=buffer;
buffer2.Cat(buffer);
buffer2.Realloc(0);
unsignedchar*pc=buffer;
constunsignedchar&c=buffer[5];
buffer[5]="O";
shorti1 =0x7FFF;
shortpi0[]={9,9,9};
shortpi1[]={1,2,3};
shortpi2[]={4,5,6};
shortpi3[]={8,8,8};
CBufferPtrT<short,10>bufferS(pi0,3);
bufferS.Cat(&i1);
bufferS.Cat(pi1,3);
bufferS.Cat(pi2,3);
bufferS.Cat(pi3,3);
CBufferPtrT<short,5>bufferS2;
bufferS2.Malloc(4);
bufferS2=bufferS;
bufferS2.Realloc(30);
CBufferPtrT<int>bufferI(5,true);
for(size_ti=0;i<bufferI.Size();i++)
bufferI[i]=i*10;
bufferI.Malloc();
bufferI[0]=123;
//下面这行编译不通过,正好说明这个类是类型安全的
//bufferI=bufferS;
return0;
}
--------------------------------------------------------------------------------
new&delete
一说到new的封装大家立马想到的就是智能指针吧!没错,就是智能指针。但STL提供的auto_ptr缺陷很多,首先使用起来不方便,竟然连这种写法都不支持:“std::auto_ptr<int>pi=newint;”,天理何在啊!更可恨的是不支持数组指针(需要delete[]),另外如果某些类重载了new操作符的话使用它也有很多问题的,还有其它的很多缺点(我忘记了^_^)。不过,C++0x似乎对智能指针作了重大改进,已经有支持引用计数的智能指针了,但不知是否解决数组指针和区分delete与::delete的问题(本座没实测,要是您知道麻烦告诉一声^_^)。无论如何,下面代码列出的智能指针支持区分delete/delete[]/::delete/::delete[]。算是auto_ptr的改良(也没有使用引用计数),文章篇幅太长了,测试用例就不发了,各位看官自行尝试吧:
/************************************************************************/
/* smart_ptr单实体或数组智能指针 */
/************************************************************************/
template<class_Ty>
structsimple_deleter
{
staticvoiddelete_ptr(_Ty*pv){deletepv;}
};
template<class_Ty>
structglobal_simple_deleter
{
staticvoiddelete_ptr(_Ty*pv){::deletepv;}
};
template<class_Ty>
structarray_deleter
{
staticvoiddelete_ptr(_Ty*pv){delete[]pv;}
};
template<class_Ty>
structglobal_array_deleter
{
staticvoiddelete_ptr(_Ty*pv){::delete[]pv;}
};
template<class_Ty,class_Deleter>
classsmart_ptr
{
public:
smart_ptr(_Ty*_Ptr=0) :_Myptr(_Ptr) {}
smart_ptr(smart_ptr<_Ty,_Deleter>&_Right) :_Myptr(_Right.release()) {}
~smart_ptr()
{
reset();
}
smart_ptr<_Ty,_Deleter>&reset(_Ty*_Ptr=0)
{
if(_Ptr!=_Myptr)
{
if(_Myptr)
_Deleter::delete_ptr(_Myptr);
_Myptr=_Ptr;
}
return*this;
}
smart_ptr<_Ty,_Deleter>&reset(smart_ptr<_Ty,_Deleter>&_Right)
{
if(this!=&_Right)
reset(_Right.release());
return*this;
}
_Ty*release()
{
_Ty*_Ptr =_Myptr;
_Myptr =0;
return_Ptr;
}
smart_ptr<_Ty,_Deleter>&operator=(_Ty*_Ptr) {returnreset(_Ptr);}
smart_ptr<_Ty,_Deleter>&operator=(smart_ptr<_Ty,_Deleter>&_Right) {returnreset(_Right);}
boolis_valid () const {return_Myptr!=0;}
_Ty&operator* () const {return*_Myptr;}
_Ty*get () const {return_Myptr;}
_Ty*operator-> () const {return_Myptr;}
operator_Ty* () const {return_Myptr;}
private:
template<class_Other>smart_ptr<_Ty,_Deleter> (constsmart_ptr<_Ty,_Other>&);
template<class_Other>smart_ptr<_Ty,_Deleter>& reset (constsmart_ptr<_Ty,_Other>&);
template<class_Other>smart_ptr<_Ty,_Deleter>& operator= (constsmart_ptr<_Ty,_Other>&);
template<class_Other>smart_ptr<_Ty,_Deleter> (constsmart_ptr<_Other,_Deleter>&);
template<class_Other>smart_ptr<_Ty,_Deleter>& reset (constsmart_ptr<_Other,_Deleter>&);
template<class_Other>smart_ptr<_Ty,_Deleter>& operator= (constsmart_ptr<_Other,_Deleter>&);
protected:
_Ty*_Myptr;
};
/************************************************************************/
/* smart_simple_ptr单实体智能指针 */
/************************************************************************/
template<class_Ty>
classsmart_simple_ptr:publicsmart_ptr<_Ty,simple_deleter<_Ty>>
{
public:
smart_simple_ptr(_Ty*_Ptr=0) :smart_ptr(_Ptr) {}
smart_simple_ptr(smart_simple_ptr<_Ty>&_Right) :smart_ptr(_Right) {}
smart_simple_ptr(smart_ptr<_Ty,simple_deleter<_Ty>>&_Right) :smart_ptr(_Right) {}
smart_simple_ptr<_Ty>&operator=(smart_ptr<_Ty,simple_deleter<_Ty>>&_Right)
{return(smart_simple_ptr<_Ty>&)__super::operator=(_Right);}
smart_simple_ptr<_Ty>&operator=(smart_simple_ptr<_Ty>&_Right)
{return(smart_simple_ptr<_Ty>&)__super::operator=(_Right);}
smart_simple_ptr<_Ty>&operator=(_Ty*_Ptr)
{return(smart_simple_ptr<_Ty>&)__super::operator=(_Ptr);}
private:
template<class_Other>smart_simple_ptr<_Ty> (constsmart_ptr<_Ty,_Other>&);
template<class_Other>smart_simple_ptr<_Ty>& operator= (constsmart_ptr<_Ty,_Other>&);
template<class_Other>smart_simple_ptr<_Ty> (constsmart_simple_ptr<_Other>&);
template<class_Other>smart_simple_ptr<_Ty>& operator= (constsmart_simple_ptr<_Other>&);
};
/************************************************************************/
/* smart_gd_simple_ptr单实体智能指针(使用全局delete) */
/************************************************************************/
template<class_Ty>
classsmart_gd_simple_ptr:publicsmart_ptr<_Ty,global_simple_deleter<_Ty>>
{
public:
smart_gd_simple_ptr(_Ty*_Ptr=0) :smart_ptr(_Ptr) {}
smart_gd_simple_ptr(smart_gd_simple_ptr<_Ty>&_Right) :smart_ptr(_Right) {}
smart_gd_simple_ptr(smart_ptr<_Ty,global_simple_deleter<_Ty>>&_Right) :smart_ptr(_Right) {}
smart_gd_simple_ptr<_Ty>&operator=(smart_ptr<_Ty,global_simple_deleter<_Ty>>&_Right)
{return(smart_gd_simple_ptr<_Ty>&)__super::operator=(_Right);}
smart_gd_simple_ptr<_Ty>&operator=(smart_gd_simple_ptr<_Ty>&_Right)
{return(smart_gd_simple_ptr<_Ty>&)__super::operator=(_Right);}
smart_gd_simple_ptr<_Ty>&operator=(_Ty*_Ptr)
{return(smart_gd_simple_ptr<_Ty>&)__super::operator=(_Ptr);}
private:
template<class_Other>smart_gd_simple_ptr<_Ty> (constsmart_ptr<_Ty,_Other>&);
template<class_Other>smart_gd_simple_ptr<_Ty>& operator= (constsmart_ptr<_Ty,_Other>&);
template<class_Other>smart_gd_simple_ptr<_Ty> (constsmart_gd_simple_ptr<_Other>&);
template<class_Other>smart_gd_simple_ptr<_Ty>& operator= (constsmart_gd_simple_ptr<_Other>&);
};
/************************************************************************/
/* smart_array_ptr数组智能指针 */
/************************************************************************/
template<class_Ty>
classsmart_array_ptr:publicsmart_ptr<_Ty,array_deleter<_Ty>>
{
public:
smart_array_ptr(_Ty*_Ptr=0) :smart_ptr(_Ptr) {}
smart_array_ptr(smart_simple_ptr<_Ty>&_Right) :smart_ptr(_Right) {}
smart_array_ptr(smart_ptr<_Ty,array_deleter<_Ty>>&_Right) :smart_ptr(_Right) {}
smart_array_ptr<_Ty>&operator=(smart_ptr<_Ty,array_deleter<_Ty>>&_Right)
{return(smart_array_ptr<_Ty>&)__super::operator=(_Right);}
smart_array_ptr<_Ty>&operator=(smart_array_ptr<_Ty>&_Right)
{return(smart_array_ptr<_Ty>&)__super::operator=(_Right);}
smart_array_ptr<_Ty>&operator=(_Ty*_Ptr)
{return(smart_array_ptr<_Ty>&)__super::operator=(_Ptr);}
private:
template<class_Other>smart_array_ptr<_Ty> (constsmart_ptr<_Ty,_Other>&);
template<class_Other>smart_array_ptr<_Ty>& operator= (constsmart_ptr<_Ty,_Other>&);
template<class_Other>smart_array_ptr<_Ty> (constsmart_array_ptr<_Other>&);
template<class_Other>smart_array_ptr<_Ty>& operator= (constsmart_array_ptr<_Other>&);
};
/************************************************************************/
/* smart_gd_array_ptr数组智能指针(使用全局delete) */
/************************************************************************/
template<class_Ty>
classsmart_gd_array_ptr:publicsmart_ptr<_Ty,global_array_deleter<_Ty>>
{
public:
smart_gd_array_ptr(_Ty*_Ptr=0) :smart_ptr(_Ptr) {}
smart_gd_array_ptr(smart_gd_array_ptr<_Ty>&_Right) :smart_ptr(_Right) {}
smart_gd_array_ptr(smart_ptr<_Ty,global_array_deleter<_Ty>>&_Right) :smart_ptr(_Right) {}
smart_gd_array_ptr<_Ty>&operator=(smart_ptr<_Ty,global_array_deleter<_Ty>>&_Right)
{return(smart_gd_array_ptr<_Ty>&)__super::operator=(_Right);}
smart_gd_array_ptr<_Ty>&operator=(smart_gd_array_ptr<_Ty>&_Right)
{return(smart_gd_array_ptr<_Ty>&)__super::operator=(_Right);}
smart_gd_array_ptr<_Ty>&operator=(_Ty*_Ptr)
{return(smart_gd_array_ptr<_Ty>&)__super::operator=(_Ptr);}
private:
template<class_Other>smart_gd_array_ptr<_Ty> (constsmart_ptr<_Ty,_Other>&);
template<class_Other>smart_gd_array_ptr<_Ty>& operator= (constsmart_ptr<_Ty,_Other>&);
template<class_Other>smart_gd_array_ptr<_Ty> (constsmart_gd_array_ptr<_Other>&);
template<class_Other>smart_gd_array_ptr<_Ty>& operator= (constsmart_gd_array_ptr<_Other>&);
};
--------------------------------------------------------------------------------
后记
•对于内存管理,其实还有一种情形还没讲的,就是如何优雅地管理vetor、list、map这类容器中的指针,这个话题留到以后讨论STL时再详细阐述吧。
•在本座的代码中基本上看不到free/delere这类单词(new则是有的——给智能指针赋值的时候^_^),就本座的经验而言,封装如果利用得当确实能减少很多麻烦,使代码更清晰,有条理,降低错误发生几率。
•当然了,封装并不是万能,它不能解决所有问题,关键是靠个人的专注与细心。
•本座码字提出自己的观点,旨在抛砖引玉,激发大家思考如何培养良好的编程习惯,不是权威,更不能尽信。最实在的知识应该来自个人最直接的体验。
相关文章
- C++构造函数的作用_c++什么是构造函数
- C++输出对象在内存中的地址
- C++精通之路:模拟实现mapu002Fset
- Rust 视界周刊 Week 5 | 驳 “Rust 等内存安全语言的安全性并不优于C++”
- C++内存模型,我们常说的堆栈究竟指什么?
- c++的链表-C++链表
- C++ Boost 内存池与智能指针
- C/C++ 获取操作系统版本
- c 线程安全的单例模式-C++单例模式(线程安全、内存释放)
- c++使用icu国际化(i18n)
- 【C++初阶】C++内存管理
- 【C++ 语言】命名空间 ( namespace | 命名空间定义 | 命名空间嵌套 | 域作用符 | 与 include 对比 )
- C&C++内存管理
- 【C++修炼之路】6. 内存管理
- c++ 内存分区模型
- C++ 中文周刊 第107期
- 【C++】内存管理
- C/C++内存对齐详解编程语言
- C++ new和delete(C++动态分配和释放内存)
- C++ complex复数类用法详解
- C/C++宏详细解析
- C++如何通过ostringstream实现任意类型转string
- C++内存泄漏及检测工具详解
- C++常用的#include头文件总结
- C++可变参数的函数与模板实例分析