--------------------------------------------------------------------------------
内存映射文件
下面的代码把 File Mapping 句柄以及从 File Mapping 映射出来的内存分别封装到 CFileMapping 和 CShareMemory 中,可以直接使用 CShareMemory 可以创建一个 File Mapping 以及映射 File Mapping 的内存。
代码如下:
class CFileMapping
{
public:
CFileMapping(
LPCTSTR lpszName,
DWORD dwMaximumSizeLow,
DWORD dwMaximumSizeHigh = 0,
HANDLE hFile = INVALID_HANDLE_VALUE,
DWORD flProtect = PAGE_READWRITE,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes = NULL
)
{
m_hMap = ::CreateFileMapping (
hFile,
lpFileMappingAttributes,
flProtect,
dwMaximumSizeHigh,
dwMaximumSizeLow,
lpszName
);
ASSERT(IsValid());
}
~CFileMapping()
{
if(IsValid())
VERIFY(::CloseHandle(m_hMap));
}
LPVOID ViewMap (
DWORD dwNumberOfBytesToMap,
DWORD dwFileOffsetLow,
DWORD dwFileOffsetHigh = 0,
DWORD dwDesiredAccess = FILE_MAP_ALL_ACCESS
)
{
return ::MapViewOfFile (
m_hMap,
dwDesiredAccess,
dwFileOffsetHigh,
dwFileOffsetLow,
dwNumberOfBytesToMap
);
}
BOOL UnViewMap(LPCVOID lpBaseAddress)
{
return ::UnmapViewOfFile(lpBaseAddress);
}
operator HANDLE () {return m_hMap;}
BOOL IsValid () {return m_hMap != NULL;}
private:
HANDLE m_hMap;
DECLARE_PRIVATE_COPY_CONSTRUCTOR(CFileMapping)
};
class CShareMemory
{
public:
CShareMemory(DWORD dwSize, LPCTSTR lpszName = NULL)
: m_fm(lpszName, dwSize)
{
ASSERT(dwSize 0);
}
~CShareMemory()
{
for(setULONG_PTR::const_iterator it = m_set.begin(); it != m_set.end(); ++it)
{
LPVOID pV = (LPVOID)*it;
ASSERT(pV);
m_fm.UnViewMap(pV);
}
m_set.clear();
}
LPVOID Alloc(DWORD dwNumberOfBytesToMap, DWORD dwFileOffsetLow)
{
LPVOID pV = m_fm.ViewMap(dwNumberOfBytesToMap, dwFileOffsetLow);
if(pV) m_set.insert((ULONG_PTR)pV);
ASSERT(pV);
return pV;
}
BOOL Free(LPCVOID lpBaseAddress)
{
ASSERT(lpBaseAddress);
setULONG_PTR::iterator it = m_set.find((ULONG_PTR)lpBaseAddress);
if(it != m_set.end())
m_set.erase(it);
return m_fm.UnViewMap(lpBaseAddress);
}
private:
CFileMapping m_fm;
setULONG_PTR m_set;
DECLARE_PRIVATE_COPY_CONSTRUCTOR(CShareMemory)
};
细心的朋友一定会发觉其实这样封装是有缺点的:首先,CShareMemory 只能做内存共享,不能映射到真实文件(hFile 永远为 INVALID_HANDLE_VALUE);第二,可以对 CShareMemory 的 Alloc() 和 Free() 方法进一步封装,利用封装类的析构函数自动调用 Free(),这样就可以完全消除 “setULONG_PTR m_set” 这个属性了;第三,CFileMapping 也可以把文件句柄一起封装进来,这样,从 CreateFile() 到 CreateFileMapping() 都受控了。这个不完美的封装就权当反面教材吧 ^_^
--------------------------------------------------------------------------------
malloc() 系列函数
很多人都建议,在 C++ 中尽量用 new 操作符取代 malloc(),因为 new 类型安全,自动调用构造函数和析构函数等等。关于这点本座略有异议,在某些情形下 malloc() 其实比 new 更好使,效率方面我们可以不计较(几乎所有编译器的 new 操作符都用 malloc() 分配内存),从事过偏底层开发的人都清楚,我们避免不了处理 row data(如:socket 的收发缓冲区等)数据,这类数据是非常适合使用 malloc() 的,用 new 分配的内存还要停顿下来想想到底是用 delete、delete[]、::delete、::delete[] 中的哪个释放,malloc() 分配的内存想都不用想,free() 包打天下,何况人家有 realloc() 可以方便地重新调整内存,你有没有 “renew” 呢?总之一句话,malloc() 的确是有存在的必要,就看接下来我们如何封装它了,请看代码:
代码如下:
// T : 数据类型(内置类型或结构体)
// MAX_CACHE_SIZE : 预申请内存的最大数目,以 sizeof(T) 为单位,如果该值设置合理,对于
// 需要动态递增缓冲区的 buffer 来说能大大提高效率
templateclass T, size_t MAX_CACHE_SIZE = 0
class CBufferPtrT
{
public:
explicit CBufferPtrT(size_t size = 0, bool zero = false) {Reset(); Malloc(size, zero);}
explicit CBufferPtrT(const T* pch, size_t size) {Reset(); Copy(pch, size);}
// 拷贝构造函数要分两种情形
CBufferPtrT(const CBufferPtrT& other) {Reset(); Copy(other);}
templatesize_t S CBufferPtrT(const CBufferPtrTT, S& other) {Reset(); Copy(other);}
~CBufferPtrT() {Free();}
T* Malloc(size_t size = 1, bool zero = false)
{
Free();
return Alloc(size, zero, false);
}
T* Realloc(size_t size, bool zero = false)
{
return Alloc(size, zero, true);
}
void Free()
{
if(m_pch)
{
free(m_pch);
Reset();
}
}
templatesize_t S CBufferPtrT& Copy(const CBufferPtrTT, S& other)
{
if((void*)&other != (void*)this)
Copy(other.Ptr(), other.Size());
return *this;
}
CBufferPtrT& Copy(const T* pch, size_t size)
{
Malloc(size);
if(m_pch)
memcpy(m_pch, pch, size * sizeof(T));
return *this;
}
// 动态扩大 buffer
templatesize_t S CBufferPtrT& Cat(const CBufferPtrTT, S& other)
{
if((void*)&other != (void*)this)
Cat(other.Ptr(), other.Size());
return *this;
}
// 动态扩大 buffer
CBufferPtrT& Cat(const T* pch, size_t size = 1)
{
size_t pre_size = m_size;
Realloc(m_size + size);
if(m_pch)
memcpy(m_pch + pre_size, pch, size * sizeof(T));
return *this;
}
templatesize_t S bool Equal(const CBufferPtrTT, S& other) const
{
if((void*)&other == (void*)this)
return true;
else if(m_size != other.Size())
return false;
else if(m_size == 0)
return true;
else
return (memcmp(m_pch, other.Ptr(), m_size * sizeof(T)) == 0);
}
bool Equal(T* pch) const
{
if(m_pch == pch)
return true;
else if(!m_pch || !pch)
return false;
else
return (memcmp(m_pch, pch, m_size * sizeof(T)) == 0);
}
T* Ptr() {return m_pch;}
const T* Ptr() const {return m_pch;}
T& Get(int i) {return *(m_pch + i);}
const T& Get(int i) const {return *(m_pch + i);}
size_t Size() const {return m_size;}
bool IsValid() const {return m_pch != 0;}
// 啊哈,竟然是类型安全的
operator T* () {return Ptr();}
operator const T* () const {return Ptr();}
// 哇塞,竟然还支持索引访问
T& operator [] (int i) {return Get(i);}
const T& operator [] (int i) const {return Get(i);}
bool operator == (T* pv) const {return Equal(pv);}
templatesize_t S bool operator == (const CBufferPtrTT, S& other) {return Equal(other);}
// 赋值操作符要分两种情形
CBufferPtrT& operator = (const CBufferPtrT& other) {return Copy(other);}
templatesize_t S CBufferPtrT& operator = (const CBufferPtrTT, S& other) {return Copy(other);}
private:
void Reset() {m_pch = 0; m_size = 0; m_capacity = 0;}
size_t GetAllocSize(size_t size) {return max(size, min(size * 2, m_size + MAX_CACHE_SIZE));}
T* Alloc(size_t size, bool zero = false, bool is_realloc = false)
{
if(size = 0 && size != m_size)
{
size_t rsize = 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));
return m_pch;
}
private:
T* m_pch;
size_t m_size;
size_t m_capacity;
};
// 常用 buffer 类型的 typedef
typedef CBufferPtrTchar CCharBufferPtr;
typedef CBufferPtrTwchar_t CWCharBufferPtr;
typedef CBufferPtrTunsigned char CByteBufferPtr;
typedef CByteBufferPtr CBufferPtr;
#ifdef _UNICODE
typedef CWCharBufferPtr CTCharBufferPtr;
#else
typedef CCharBufferPtr CTCharBufferPtr;
#endif
嗯。这里要解释一下为何需要两个拷贝构造函数和赋值操作符重载,首先,编译器为不同的模板参数生成不同的类,也就是说:CBufferPtrTint, 1 和 CBufferPtrTint, 2 被看作是不同的类,另外,C++ 编译器为每个类提供了提供了拷贝构造函数和赋值操作符重载的默认实现(浅拷贝)。因此,上述的第一组拷贝构造函数和赋值操作符重载是改写编译器的默认实现,第二组拷贝构造函数和赋值操作符重载是处理其它类到本类的转换。
本座对这个封装灰常满意(唯一美中不足的就是 cnblogs 的编辑器太坑爹了,把代码弄乱 ^_^),它并非只是一个普通的 malloc() 封装,完全能可以把它看作是一种“支持索引访问的类型安全的动态缓冲区”。如果把它放在一个 socket 通信类中作为成员属性,充当跨越多个线程和多个方法访问的接收缓冲区和发送缓冲区的角色就最适合不过了(当然要自己做同步了)。大家可以调试一下下面的测试例子,了解一下它的用法:
代码如下:
测试用例
int _tmain(int argc, _TCHAR* argv[])
{
CBufferPtr buffer;
unsigned char c1 = 'X';
unsigned char pc1[] = "123";
unsigned char pc2[] = "abc";
buffer.Cat(&c1);
buffer.Cat(pc1, 3);
buffer.Cat(pc2, 3);
CBufferPtrTunsigned char, 10 buffer2 = buffer;
buffer2.Cat(buffer);
buffer2.Realloc(0);
unsigned char* pc = buffer;
const unsigned char& c = buffer[5];
buffer[5] = 'O';
short i1 = 0x7FFF;
short pi0[] = {9,9,9};
short pi1[] = {1,2,3};
short pi2[] = {4,5,6};
short pi3[] = {8,8,8};
CBufferPtrTshort, 10 bufferS(pi0, 3);
bufferS.Cat(&i1);
bufferS.Cat(pi1, 3);
bufferS.Cat(pi2, 3);
bufferS.Cat(pi3, 3);
CBufferPtrTshort, 5 bufferS2;
bufferS2.Malloc(4);
bufferS2 = bufferS;
bufferS2.Realloc(30);
CBufferPtrTint bufferI(5, true);
for(size_t i = 0; i bufferI.Size(); i++)
bufferI[i] = i *10;
bufferI.Malloc();
bufferI[0] = 123;
// 下面这行编译不通过,正好说明这个类是类型安全的
// bufferI = bufferS;
return 0;
}
--------------------------------------------------------------------------------
new & delete
一说到 new 的封装大家立马想到的就是智能指针吧!没错,就是智能指针。但 STL 提供的 auto_ptr 缺陷很多,首先使用起来不方便,竟然连这种写法都不支持:“std::auto_ptrint pi = new int;”,天理何在啊!更可恨的是不支持数组指针(需要 delete[]),另外如果某些类重载了 new 操作符的话使用它也有很多问题的,还有其它的很多缺点(我忘记了 ^_^)。不过,C++0x 似乎对智能指针作了重大改进,已经有支持引用计数的智能指针了,但不知是否解决数组指针和区分 delete 与 ::delete 的问题(本座没实测,要是您知道麻烦告诉一声 ^_^)。无论如何,下面代码列出的智能指针支持区分 delete / delete[] / ::delete / ::delete[]。算是 auto_ptr 的改良(也没有使用引用计数),文章篇幅太长了,测试用例就不发了,各位看官自行尝试吧:
代码如下:
/************************************************************************/
/* smart_ptr 单实体或数组智能指针 */
/************************************************************************/
templateclass _Ty
struct simple_deleter
{
static void delete_ptr(_Ty* pv) {delete pv;}
};
templateclass _Ty
struct global_simple_deleter
{
static void delete_ptr(_Ty* pv) {::delete pv;}
};
templateclass _Ty
struct array_deleter
{
static void delete_ptr(_Ty* pv) {delete[] pv;}
};
templateclass _Ty
struct global_array_deleter
{
static void delete_ptr(_Ty* pv) {::delete[] pv;}
};
templateclass _Ty, class _Deleter
class smart_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) {return reset(_Ptr);}
smart_ptr_Ty, _Deleter& operator = (smart_ptr_Ty, _Deleter& _Right) {return reset(_Right);}
bool is_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:
templateclass _Other smart_ptr_Ty, _Deleter (const smart_ptr_Ty, _Other&);
templateclass _Other smart_ptr_Ty, _Deleter& reset (const smart_ptr_Ty, _Other&);
templateclass _Other smart_ptr_Ty, _Deleter& operator = (const smart_ptr_Ty, _Other&);
templateclass _Other smart_ptr_Ty, _Deleter (const smart_ptr_Other, _Deleter&);
templateclass _Other smart_ptr_Ty, _Deleter& reset (const smart_ptr_Other, _Deleter&);
templateclass _Other smart_ptr_Ty, _Deleter& operator = (const smart_ptr_Other, _Deleter&);
protected:
_Ty* _Myptr;
};
/************************************************************************/
/* smart_simple_ptr 单实体智能指针 */
/************************************************************************/
templateclass _Ty
class smart_simple_ptr : public smart_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:
templateclass _Other smart_simple_ptr_Ty (const smart_ptr_Ty, _Other&);
templateclass _Other smart_simple_ptr_Ty& operator = (const smart_ptr_Ty, _Other&);
templateclass _Other smart_simple_ptr_Ty (const smart_simple_ptr_Other&);
templateclass _Other smart_simple_ptr_Ty& operator = (const smart_simple_ptr_Other&);
};
/************************************************************************/
/* smart_gd_simple_ptr 单实体智能指针 (使用全局 delete) */
/************************************************************************/
templateclass _Ty
class smart_gd_simple_ptr : public smart_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:
templateclass _Other smart_gd_simple_ptr_Ty (const smart_ptr_Ty, _Other&);
templateclass _Other smart_gd_simple_ptr_Ty& operator = (const smart_ptr_Ty, _Other&);
templateclass _Other smart_gd_simple_ptr_Ty (const smart_gd_simple_ptr_Other&);
templateclass _Other smart_gd_simple_ptr_Ty& operator = (const smart_gd_simple_ptr_Other&);
};
/************************************************************************/
/* smart_array_ptr 数组智能指针 */
/************************************************************************/
templateclass _Ty
class smart_array_ptr : public smart_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:
templateclass _Other smart_array_ptr_Ty (const smart_ptr_Ty, _Other&);
templateclass _Other smart_array_ptr_Ty& operator = (const smart_ptr_Ty, _Other&);
templateclass _Other smart_array_ptr_Ty (const smart_array_ptr_Other&);
templateclass _Other smart_array_ptr_Ty& operator = (const smart_array_ptr_Other&);
};
/************************************************************************/
/* smart_gd_array_ptr 数组智能指针 (使用全局 delete) */
/************************************************************************/
templateclass _Ty
class smart_gd_array_ptr : public smart_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:
templateclass _Other smart_gd_array_ptr_Ty (const smart_ptr_Ty, _Other&);
templateclass _Other smart_gd_array_ptr_Ty& operator = (const smart_ptr_Ty, _Other&);
templateclass _Other smart_gd_array_ptr_Ty (const smart_gd_array_ptr_Other&);
templateclass _Other smart_gd_array_ptr_Ty& operator = (const smart_gd_array_ptr_Other&);
};
--------------------------------------------------------------------------------
后记
• 对于内存管理,其实还有一种情形还没讲的,就是如何优雅地管理 vetor、list、map 这类容器中的指针,这个话题留到以后讨论 STL 时再详细阐述吧。
•在本座的代码中基本上看不到 free / delere 这类单词(new 则是有的 —— 给智能指针赋值的时候 ^_^),就本座的经验而言,封装如果利用得当确实能减少很多麻烦,使代码更清晰,有条理,降低错误发生几率。
•当然了,封装并不是万能,它不能解决所有问题,关键是靠个人的专注与细心。
•本座码字提出自己的观点,旨在抛砖引玉,激发大家思考如何培养良好的编程习惯,不是权威,更不能尽信。最实在的知识应该来自个人最直接的体验。