如何養成良好的 C++ 編程習慣(一)—— 記憶體管理

来源:http://www.cnblogs.com/shouce/archive/2016/04/05/5353686.html
-Advertisement-
Play Games

開篇導讀 “養成良好的編程習慣”其實是相當綜合的一個命題,可以從多個角度、維度和層次進行論述和評判。如代碼的風格、效率和可讀性;模塊設計的靈活性、可擴展性和耦合度等等。要試圖把所有方面都闡述清楚必須花很多的精力,而且也不一定能闡述得全面。因此,本系列文章以軟體開發的基礎問題為切入點,闡述程式設計和代 ...


開篇導讀

  “養成良好的編程習慣”其實是相當綜合的一個命題,可以從多個角度、維度和層次進行論述和評判。如代碼的風格、效率和可讀性;模塊設計的靈活性、可擴展性和耦合度等等。要試圖把所有方面都闡述清楚必須花很多的精力,而且也不一定能闡述得全面。因此,本系列文章以軟體開發的基礎問題為切入點,闡述程式設計和代碼編寫方面的細節問題,以點帶面,旨在激發大家的思考與總結,希望能為大家帶來實際的幫助。         

  雖然本系列文章定位為科普讀物,但本座相信它們不但適合新手們學習借鑒,同時也能引發老鳥們的反思與共鳴。歡迎大家提出寶貴的意見和反饋 ^_^

  在開篇講述本章主要內容之前,本座首先用小小篇幅論述一下一種良好的工作習慣 —— 積累、提煉與求精。在工作和學習的過程中,不斷把學到的知識通過有效的方式積累起來,形成自己的知識庫,隨著知識量的擴大,就會得到從量變到質變的提升。另外還要不斷地對知識進行提煉,隨著自己知識面的擴大以及水平的提升,你肯定會發現原有知識庫存在著一些片面、局限、笨拙甚至錯誤。這時,就需要你有精益求精精的態度和毅力對知識庫進行優化整理。

  也許以上這些各位都曾想過去實施,也明白其中的道理,但是自己就是給自己各種堂而皇之的藉口不花時間去做。這樣說吧,技術之路不好走,這個行業有兩項基本要求: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 視窗無盡的 Memory Leak 警告,程式詭異的 0X00000004 指針異常,仿佛回到那一年我們一起哭過的日子,你 Hold 得住嗎?其實,現實並沒有你想的那麼糟糕。只要你付出一點點,花一點點心思,沒錯!就一點點而已 —— 用 C++ 類封裝記憶體訪問,就會解決你大部分的煩惱,讓你受益終身。以 Windows 程式為例,主要有以下幾種記憶體管理方式:

  • 虛擬記憶體(Virtual Memory)
  • 預設堆私有堆(Process Heap & Private Heap)
  • 記憶體映射文件(File Mapping)
  • 進程堆棧(Heap,其實就是用 malloc() 或 預設的 new 操作符在 Process Heap 里一小塊一小塊地割肉 ^_^)
  • (Stack,記憶體由調用者或被調用者自動管理)

   今天我們的主題是封裝,至於每種記憶體模型的概念和 API 的使用方式在這裡就不講了,Google 一下就知道。其實用 C++ 封裝上述前 4 種記憶體訪問的原理都差不多,就是在構造函數或其他操作函數中分配記憶體,然後再在析構函數中確保記憶體被正確釋放。虛擬記憶體、預設堆和私有堆的操作方式相似,這裡就不一一展示了,有興趣的朋友可以參考本座前幾天發表的那篇無人問津的文章:《C++ 封裝私有堆(Private Heap)》,哎!下麵對記憶體映射文件的封裝也只稍作介紹、我們主要討論的是使用頻率最高的 malloc() 和 new 的封裝。


 

 記憶體映射文件

  下麵的代碼把 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(set<ULONG_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);

set<ULONG_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;
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() 分配記憶體),從事過偏底層開發的人都清楚,我們避免不了處理 row data(如:socket 的收發緩衝區等)數據,這類數據是非常適合使用 malloc() 的,用 new 分配的記憶體還要停頓下來想想到底是用 delete、delete[]、::delete、::delete[] 中的哪個釋放,malloc() 分配的記憶體想都不用想,free() 包打天下,何況人家有 realloc() 可以方便地重新調整記憶體,你有沒有 “renew” 呢?總之一句話,malloc() 的確是有存在的必要,就看接下來我們如何封裝它了,請看代碼:

 

複製代碼
// T                 : 數據類型(內置類型或結構體)
// MAX_CACHE_SIZE : 預申請記憶體的最大數目,以 sizeof(T) 為單位,如果該值設置合理,對於
// 需要動態遞增緩衝區的 buffer 來說能大大提高效率
template<class 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);}
template<size_t S> CBufferPtrT(const CBufferPtrT<T, 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();
}
}

template<size_t S> CBufferPtrT& Copy(const CBufferPtrT<T, 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
template<size_t S> CBufferPtrT& Cat(const CBufferPtrT<T, 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;
}

template<size_t S> bool Equal(const CBufferPtrT<T, 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);}
template<size_t S> bool operator == (const CBufferPtrT<T, S>& other) {return Equal(other);}
// 賦值操作符要分兩種情形
CBufferPtrT& operator = (const CBufferPtrT& other) {return Copy(other);}
template<size_t S> CBufferPtrT& operator = (const CBufferPtrT<T, 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 CBufferPtrT<char> CCharBufferPtr;
typedef CBufferPtrT<wchar_t> CWCharBufferPtr;
typedef CBufferPtrT<unsigned char> CByteBufferPtr;
typedef CByteBufferPtr CBufferPtr;

#ifdef _UNICODE
typedef CWCharBufferPtr CTCharBufferPtr;
#else
typedef CCharBufferPtr CTCharBufferPtr;
#endif
複製代碼


  嗯。這裡要解釋一下為何需要兩個拷貝構造函數和賦值操作符重載,首先,編譯器為不同的模板參數生成不同的類,也就是說:CBufferPtrT<int, 1> 和 CBufferPtrT<int, 2> 被看作是不同的類,另外,C++ 編譯器為每個類提供了提供了拷貝構造函數和賦值操作符重載的預設實現(淺拷貝)。因此,上述的第一組拷貝構造函數和賦值操作符重載是改寫編譯器的預設實現,第二組拷貝構造函數和賦值操作符重載是處理其它類到本類的轉換。

  本座對這個封裝灰常滿意(唯一美中不足的就是 cnblogs 的編輯器太坑爹了,把代碼弄亂 ^_^),它並非只是一個普通的 malloc() 封裝,完全能可以把它看作是一種“支持索引訪問的類型安全的動態緩衝區”。如果把它放在一個 socket 通信類中作為成員屬性,充當跨越多個線程和多個方法訪問的接收緩衝區和發送緩衝區的角色就最適合不過了(當然要自己做同步了)。大家可以調試一下下麵的測試例子,瞭解一下它的用法:

 

測試用例
 1 int _tmain(int argc, _TCHAR* argv[])
2 {
3 CBufferPtr buffer;
4
5 unsigned char c1 = 'X';
6 unsigned char pc1[] = "123";
7 unsigned char pc2[] = "abc";
8 buffer.Cat(&c1);
9 buffer.Cat(pc1, 3);
10 buffer.Cat(pc2, 3);
11
12 CBufferPtrT<unsigned char, 10> buffer2 = buffer;
13 buffer2.Cat(buffer);
14 buffer2.Realloc(0);
15
16 unsigned char* pc = buffer;
17 const unsigned char& c = buffer[5];
18 buffer[5] = 'O';
19
20 short i1 = 0x7FFF;
21 short pi0[] = {9,9,9};
22 short pi1[] = {1,2,3};
23 short pi2[] = {4,5,6};
24 short pi3[] = {8,8,8};
25
26 CBufferPtrT<short, 10> bufferS(pi0, 3);
27
28 bufferS.Cat(&i1);
29 bufferS.Cat(pi1, 3);
30 bufferS.Cat(pi2, 3);
31 bufferS.Cat(pi3, 3);
32
33 CBufferPtrT<short, 5> bufferS2;
34 bufferS2.Malloc(4);
35
36 bufferS2 = bufferS;
37 bufferS2.Realloc(30);
38
39 CBufferPtrT<int> bufferI(5, true);
40
41 for(size_t i = 0; i < bufferI.Size(); i++)
42 bufferI[i] = i *10;
43
44 bufferI.Malloc();
45 bufferI[0] = 123;
46
47 // 下麵這行編譯不通過,正好說明這個類是類型安全的
48 // bufferI = bufferS;
49
50 return 0;
51 }

 


 

new & delete

  一說到 new 的封裝大家立馬想到的就是智能指針吧!沒錯,就是智能指針。但 STL 提供的 auto_ptr 缺陷很多,首先使用起來不方便,竟然連這種寫法都不支持:“std::auto_ptr<int> pi = new int;”,天理何在啊!更可恨的是不支持數組指針(需要 delete[]),另外如果某些類重載了 new 操作符的話使用它也有很多問題的,還有其它的很多缺點(我忘記了 ^_^)。不過,C++0x 似乎對智能指針作了重大改進,已經有支持引用計數的智能指針了,但不知是否解決數組指針和區分 delete 與 ::delete 的問題(本座沒實測,要是您知道麻煩告訴一聲 ^_^)。無論如何,下麵代碼列出的智能指針支持區分 delete / delete[] / ::delete / ::delete[]。算是 auto_ptr 的改良(也沒有使用引用計數),文章篇幅太長了,測試用例就不發了,各位看官自行嘗試吧:

複製代碼
/************************************************************************/
/* smart_ptr 單實體或數組智能指針 */
/************************************************************************/

template<class _Ty>
struct simple_deleter
{
static void delete_ptr(_Ty* pv) {delete pv;}
};

template<class _Ty>
struct global_simple_deleter
{
static void delete_ptr(_Ty* pv) {::delete pv;}
};

template<class _Ty>
struct array_deleter
{
static void delete_ptr(_Ty* pv) {delete[] pv;}
};

template<class _Ty>
struct global_array_deleter
{
static void delete_ptr(_Ty* pv) {::delete[] pv;}
};

template<class _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:
template<class _Other> smart_ptr<_Ty, _Deleter> (const smart_ptr<_Ty, _Other>&);
template<class _Other> smart_ptr<_Ty, _Deleter>& reset (const smart_ptr<_Ty, _Other>&);
template<class _Other> smart_ptr<_Ty, _Deleter>& operator = (const smart_ptr<_Ty, _Other>&);

template<class _Other> smart_ptr<_Ty, _Deleter> (const smart_ptr<_Other, _Deleter>&);
template<class _Other> smart_ptr<_Ty, _Deleter>& reset (const smart_ptr<_Other, _Deleter>&);
template<class _Other> smart_ptr<_Ty, _Deleter>& operator = (const smart_ptr<_Other, _Deleter>&);

protected:
_Ty* _Myptr;
};


/************************************************************************/
/* smart_simple_ptr 單實體智能指針 */
/************************************************************************/

template<class _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:
template<class _Other> smart_simple_ptr<_Ty> (const smart_ptr<_Ty, _Other>&);
template<class _Other> smart_simple_ptr<_Ty>& operator = (const smart_ptr<_Ty, _Other>&);

template<class _Other> smart_simple_ptr<_Ty> (const smart_simple_ptr<_Other>&);
template<class _Other> smart_simple_ptr<_Ty>& operator = (const smart_simple_ptr<_Other>&);
};

/************************************************************************/
/* smart_gd_simple_ptr 單實體智能指針 (使用全局 delete) */
/************************************************************************/

template<class _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:
template<class _Other> smart_gd_simple_ptr<_Ty> (const smart_ptr<_Ty, _Other>&);
template<class _Other> smart_gd_simple_ptr<_Ty>& operator = (const smart_ptr<_Ty, _Other>&);

template<class _Other> smart_gd_simple_ptr<_Ty> (const smart_gd_simple_ptr<_Other>&);
template<class _Other> smart_gd_simple_ptr<_Ty>& operator = (const smart_gd_simple_ptr<_Other>&);
};

/************************************************************************/
/* smart_array_ptr 數組智能指針 */
/************************************************************************/

template<class _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:
template<class _Other> smart_array_ptr<_Ty> (const smart_ptr<_Ty, _Other>&);
template<class _Other> smart_array_ptr<_Ty>& operator = (const smart_ptr<_Ty, _Other>&);

template<class _Other> smart_array_ptr<_Ty> (const smart_array_ptr<_Other>&);
template<class _Other> smart_array_ptr<_Ty>& operator = (const smart_array_ptr<_Other>&);
};

/************************************************************************/
/* smart_gd_array_ptr 數組智能指針 (使用全局 delete) */
/************************************************************************/

template<class _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:
template<class _Other> smart_gd_array_ptr<_Ty> (const smart_ptr<_Ty, _Other>&);
template<class _Other> smart_gd_array_ptr<_Ty>& operator = (const smart_ptr<_Ty, _Other>&);

template<class _Other> smart_gd_array_ptr<_Ty> (const smart_gd_array_ptr<_Other>&);
template<class _Other> smart_gd_array_ptr<_Ty>& operator = (const smart_gd_array_ptr<_Other>&);
};
複製代碼

 



 後記

  •  對於記憶體管理,其實還有一種情形還沒講的,就是如何優雅地管理 vetor、list、map 這類容器中的指針,這個話題留到以後討論 STL 時再詳細闡述吧。
  • 在本座的代碼中基本上看不到 free / delere 這類單詞(new 則是有的 —— 給智能指針賦值的時候 ^_^),就本座的經驗而言,封裝如果利用得當確實能減少很多麻煩,使代碼更清晰,有條理,降低錯誤發生幾率。
  • 當然了,封裝並不是萬能,它不能解決所有問題,關鍵是靠個人的專註與細心。
  • 本座碼字提出自己的觀點,旨在拋磚引玉,激發大家思考如何培養良好的編程習慣,不是權威,更不能盡信。最實在的知識應該來自個人最直接的體驗。

 

   敬請期待:《如何養成良好的 C++ 編程習慣(二)—— Windows 資源管理


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 對於使用FX2的用戶,可以升級到FX2LP,上述的應用筆記《AN4078-C》中就講解了在升級中的註意事項。 必要的修改: 1.晶振的匹配電容需要更改,FX2LP是12pF,不過筆者最近做的最小系統板,用的晶振匹配電容是22pF,沒問題,反而用12pF晶振不能穩定的工作。 2.reset引腳,確認是 ...
  • 準備的文件 新建虛擬機 選擇新建一個空的虛擬機 選擇linux和centos 分配20G的硬碟空間 ' 修改配置 調整記憶體空間 橋接:虛擬機和真實機通訊使用的是真實機的網卡,要占用真是IP NAT:虛擬網卡VMnet8 HOST ONLY :VMnet1 只能和虛擬機通訊,不能上網 安裝CentOS ...
  • 在Linux中,主要編輯器為vi或者vim,本文圍繞vim做簡單的講解說明: Linux預設自帶vi(vim)編輯器,其程式包為: vim 編輯器模式切換: 命令模式 、命令行模式、編輯模式 命令模式: 字元操作 i 當前字元之前插入 I 行首插入 a 當前字元之後插入 A 行尾插入 esc 退出當 ...
  • PHP的語言規範: 1、php中的變數名區分大小寫,但是函數名,類名,方法名,不區分大小寫,但建議區分大小寫 2、php代碼必須書寫在(php標簽),開啟標記(<?php)中間不能空格 3、php代碼每一行以分號結束,最後一行可以省略分號。 4、如果一個Php文件是由純 php代碼組成,那麼php結... ...
  • i = 0def myFun(): global i i=i +1 return i myFun() accumulate( ) total = 0def accumulate(): global total total += 1 return total ...
  • oauth應該屬於security的一部分。關於oauth的的相關知識可以查看阮一峰的文章:http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html 一、目標 現在很多系統都支持第三方賬號密碼等登陸我們自己的系統,例如:我們經常會看到,一些系統使用微 ...
  • 最近遇到的問題小結: 1.django 工程內不要有與項目名稱相同的文件。會導致無法import settings.py文件。 2.django 的 csrf 問題,當發送post請求時,會要求同時發送csrf token,是為了防止跨站請求偽造。 具體使用方法見官方文檔。 http://docs. ...
  • 介紹 我發現了一個問題,今天與大家分享。我把整個過程描述一下。 問題 問題 公司有個框架是基於smarty寫的,我負責php的升級,維護人員把新環境布上來之後,測試人員找我提出經常報錯(錯誤:提示找不到文件的)。 我追蹤了一下代碼,原來是smarty的這個地方報的錯誤。 錯誤:這裡報出文件不存在。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...