Effective C++ 學習筆記 (1)

Effective C++ 學習一 從c語言世界來到C++世界~~

Item1 優先使用const 和inline來取代#define
這個準則應該理解成優先依靠compiler而不是依靠preprocessor來檢查程序的錯誤。
定義一個常量的格式 const int NUM_LIMIT = 100;
1  當定義常量指針的時候,事情略微變得複雜
    const char * pConst const = "is a constant pointer points to const";
2 定義一個類的常量成員變得簡單。
class GamePlayer{
private :
    static const int NUM_TURNS = 5;//注意這裏的NUM_TURNS僅是個變量的聲明
    int scores[NUM_TURNS];
};
在函數的實現部分加入NUM_TURNS 的定義,這是必須,否則linker在link stage會提示出錯。
const int GamePlayer::NUM_TURNS;

3 在你需要在class A編譯階段使用class A constant時候
如 
class GamePlayer {
private:
        enum {NUM_TURNS = 5};
        
        int scores[NUM_TURNS];
......
}

#define max(a, b) ((a) > (b) ? (a) : (b))
宏定義一些簡單常用函數,可以減少調用函數的開銷, 它同時has many drawbacks, 
int a = 5, b = 10;
max(++a, b);
max(++a, b+10);
看看會有什麼奇怪的事情發生
用inline來代替macro define定義。
inline int max(int a, int b) { return a > b ? a : b; }
更近一步使用,使用Generic programming:
template<class T>
inline const T& max(const T& a, const T& b) {return a > b ? a : b;}

Item 2 優先使用iostream 而不是stdio.h。
1 iostream提供了更好的擴展性和類型安全的。

<iostream>和<iostream.h>
iostream是將std命名空間的成員引入程序,而iostream.h將global namespace中的成員引入,這樣可能會導致namespace 污染。

Item 7 準備好內存溢出情況發生後如何處理
當使用new操作符分配堆內存的時候, 如果可用內村用光, 則會拋出bad_alloc 異常。 bad_alloc異常是有operator new導致的異常,他在內存請求不能被滿足的時候(內存用光)被拋出。
按照c風格,你可能定義一個宏來處理out of memory異常,如
#define NEW (PTR, TYPE) try {PTR=new TYPE;} catch (std::bad_alloc&) {assert(0);}
ps: 其中assert是一個宏其對應的h文件爲c <assert.h>和c++ <cassert> 該宏檢查傳遞給它的表達式是否是非零, 如果是零,則返回一個出錯信息並且調用abort中止程序執行。

但是這樣就夠了嗎? 答案是不夠。因爲它忽略了new的多種分配內存空間的方法, 可以想到的是
new TYPE; new TYPE(construction parameters); new TYPE[buffer_length]
還有更加複雜的情況, 因爲在C++中,用戶可以自己定義其operator new的行爲,這樣算來, 可能的情況何止區區3,4種。所以這種方式不能滿足我們的需求。
那應該怎麼做呢?
答案是通過定義自己的out of memory handler函數來處理out of memory異常。調用set_new_handler【在<new>頭文件中定義】
set_new_handler的聲明大概是這個樣子:
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();

set_new_handler的使用方式如下:
定義你自己的handler函數, 然後裝載到Global namespace當中去。
void noMoreMemory() {
    cerr<< "Unable to satisfy the memory request.";
    abort();
}

int main()
{
    set_new_handler(noMoreMemory);
    int * p = new int[10000000];
}
這樣當請求內存不能被滿足的時候, 首先打印出錯誤信息, 然後纔會abort 程序。這樣就比簡單的core dump(清空寄存器,信息轉存)要好的多。

可以裝載新的handler就一樣可以卸載handler, 具體:
set_new_handler(null);

當operator new不能分配足夠的內存時, 它會重複執行調用handler function,直到內存請求可以被滿足爲止.
所以設計良好的handler函數應該可以完成以下動作中的一種:
1 分配更多內存或會使分配的內存以滿足內存請求
2 安裝不同的handler函數來處理該請求。 如果當前的handler函數不能處理當前的請求,但是它知道其它的handler函數可以處理這個請求, 所以它會裝載另外一個handler函數。這樣,在下次該請求不能被滿足的時候,
新安裝的handler function就會被調用來處理該請求。
3 卸載handler函數, set_new_handler(null),這樣在處理out of memory時將拋出異常bad_alloc.
4 Throw exception: 如果自定義exception,需要繼承bad_alloc,使其形成類層次結構。
5 not return: 默認行爲是abort 或 exit.

對應於類來說:
class X {
public :
    static new_handler set_new_handler(new_handler p);
    static void * operator new (size_t size);
private:
    static new_handler currentHandler;
}

new_handler X::currentHandler;
new_handler X::set_new_handler(new_handler p){
    new_handler oldHandler = currentHandler;
    currentHandler = p;
    return oldHandler;
    
}

而對於X的operator new 的動作:
1 調用global的set_new_handler,來加載X的currentHandler
2 調用global的new操作, ::new 來進行內存分配。 如果內存分配不能滿足,global new operator將會調用X的handler, 也就是剛被裝載成爲global 的handler, 如果new最終都不能滿足內存分配請求, 它將拋出bad_alloc異常, 會被捕獲, 同時會重新裝載原來的global new-handler.並且重新拋出異常
3 如果分配請求成功 X的operator new會重新裝載原來的global new handler.

在考慮一下, X的out of memory處理和X本身無關, 可以使用繼承和template來生成可以重用的代碼。
template <class T>
class NewHandlerSupport {
public:
    static new_handler set_new_handler (new_handler p);
    static void* operator new (size_t size);
private :
    static new_handler currentHandler;
}

template <class T>
new_handler NewHandlerSupport<T>::set_new_handler(new_handler p)
{
    new_handler oldHandler = currentHandler;
    currentHandler = p;
    return currentHandler;
}

template<class T>
void * NewHandlerSupport<T>::operator new (size_t t )
{
    new_handler globalHandler = std::set_new_handler(currentHandler);
    void * memory;

    try {
        memory = ::operator new (t);
    } catch (bad_alloc&) {
        std::set_new_handler(globalHandler);
        throw;
    }
    std::set_new_handler(globalHandler);
    return memory;
}


爲Class X添加自己的set_new_handler就變成

class X: public NewHandlerSupport<X> {
    ....
}

對於new operator, 直到1993 ,對應於new 的out of memory, new在調用handler函數後 返回空指針(0)而不是拋出異常。支持這張形式的定義是Widget* wp = new (nothrow) Widget();

 












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章