Effective C++ 读书笔记(八)
8 定制new和delete
條款49:了解new-handler的行為
new_handler ?set_new_handler (new_handler new_p) ?throw();
Sets ?new_p ?as the new handler function, ?the old one is returned.
operator new拋出異常以反映一個未獲滿足的內存需求之前,它會先調用一個客戶指定的錯誤處理函數,客戶必須調用set_new_handler設定。
class NewHandlerHolder{
explicit NewHandlerHolder (std::new_handler? nh):handler(nh){}
~NewHandlerHolder(){std::set_new_handler(handler);}
private:
???????? std::new_handler? handler;
};
?
class Widget{
public:
???????? static? std::new_handler set_new_handler(std::new_handler? p) throw();
???????? static? void? *operator new(std::size_t? size)throw(std::bad_alloc);
private:
???????? static? std::new_handler? currentHandler;
};
std::new_handler ?Widget::currentHandler = 0;
std::new_handler? Widget::set_new_handler (std::new_handler? p) throw
{
std::new_handler? oldHandler = currentHandler;
currentHandler? = p;
return oldHandler;
}
void*? Widget:: operator new (std::size_t? size) throw
{
NewHandlerHolder h( std::set_new_handler(currentHandler) );
return ::operator new(size);
}
?
使用:
void outOfMem();
Widget::set_new_handler(outOfMem);
Widget* pw1 = new Widget;
?
條款50:了解new和delete的合理替換時機
略
條款51:編寫new和delete時需固守常規
實現一致性operator new必得返回正確的值,內存不足時必得調用new-handling函數,必須有對付0內存需求的準備。operator new返回值十分單純,如果它有能力供應客戶申請的內存,就返回一個指針指向那塊內存,如果沒有那個能力,拋出一個bad_alloc異常。
???????? 然而也不是非常單純,因為operator new實際上不只一次嘗試分配內存,并在每次失敗后調用new-handling函數。這里假設new-handling函數能夠做某些動作將內存釋放出來,只有當new-handling指針為null時,operator new才會拋出異常。
下面是non-member operator new?偽碼:
void * operator new(std::size_t? size) throw(std::bad_alloc)
{
???????? using namespace std;
???????? if(size == 0) size =1;
???????? while(true){
???????? 嘗試分配size bytes;
???????? if 分配成功
?????????????????? return 一個指針指向分配的內存
???????? new_handler? globalHandler? =? set_new_handler(0);
???????? set_new_handler( globalHandler);
???????? if( globalHandler ) (*globalHandler)();
???????? else throw std::bad_alloc();
}
}
?
許多人沒有考慮operator new成員函數會被derived class繼承。一旦被繼承base class的operator new被調用以分配derived class對象。例如:
class Base{
public:? static void * operator new(std::size_t? size) throw(std::bad_alloc);
???????? …
}
class Derived: public Base { … }
Derived* p = new Derived; //調用Base::operator new
解決方法:
void * Base::operator new(std::size_t? size) throw(std::bad_alloc)
{
???????? if(size != sizeof(Base) ) return ::operator new(size);
???????? …
}
?
條款52:寫了placement new也要寫placement delete
如果operator new接受的參數除了一定會有的那個size_t之外還有其他,這便是所謂的placement new。眾多placement new版本中特別有用的一個是“接受一個指針指向對象該被構造之處”,那樣的operator new聲明如下:
???????? void * operator new(std::size_t, void *)throw();
對于以下代碼:
class Widget{
public:
???????? static void* operator new(std::size_t size, std::ostream& logStream) throw(std::bad_alloc);
???????? static void? operator delete(void* pMemory, std::size_t size)throw( );
???????? …
};
上述代碼存在微妙的內存泄漏。它在動態創建一個Widget時將相關分配信息志記于cerr:
???????? Widget* pw = new (std::cerr) Widget;
如果內存分配成功,但Widget構造函數拋出異常,運行系統有責任取消operator new的分配。運行系統會尋找參數個數和類型都 與operator new相同 的某個operator delete,如果找到,那就是它的調用對象。
class Widget{
public:
???????? static void* operator new(std::size_t size, std::ostream& logStream) throw(std::bad_alloc);
???????? static void? operator delete(std::size_t size, std::ostream& logStream)throw( );
static void? operator delete(void* pMemory) throw( );
???????? …
};
Widget* pw = new (std::cerr) Widget; //不再泄漏
但 delete pw;//調用正常的operator delete,而非 placement版本。
因此,我們必須同時提供一個正常的operator delete和一個placement版本(參數必須和operator new一樣)。只要這樣做,就不會有難以察覺的內存泄漏了。
?
另外,考慮到成員函數的名稱會掩蓋其外因作用域中的相同名稱,例如:
class Base{
public:
???????? static void* operator new(std::size_t size, std::ostream& logStream) throw(std::bad_alloc);
???????? …
};
Base* pb = new Base; //error,掩蓋
Base* pb = new (std::cerr) Base; //OK
同理,derived classes 中的operator new掩蓋global版本和繼承版。
class Derived: public Base{
public:
???????? static void* operator new(std::size_t size) throw(std::bad_alloc);
???????? …
};
Derived* pd = new (std::clog) Derived; //error,掩蓋
Derived* pd = new Derived;
解決辦法:建立一個base class,內含所有正常形式的new 和 delete。
?
若想以自定義形式擴展標準形式的客戶,可利用繼承機制及using聲明式取得標準形式。
?
轉載于:https://www.cnblogs.com/dachengxu/archive/2012/11/21/2781669.html
總結
以上是生活随笔為你收集整理的Effective C++ 读书笔记(八)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Threejs模仿实现滴滴官网首页地球动
- 下一篇: 【VC6.0】getline需要输入2次