青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 13, comments - 4, trackbacks - 0, articles - 0
  C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

Imperfect C++ 讀書筆記(三)

Posted on 2008-11-20 00:28 Batiliu 閱讀(395) 評論(0)  編輯 收藏 引用 所屬分類: 讀書筆記

數組和指針

首先我們來看一個經典的C/C++求數組元素個數的解決方案:

#define NUM_ELEMENTS(x) (sizeof((x)) / sizeof((x)[0]))

利用C/C++編譯器將表達式 ar[n] 在編譯器解釋為 *(ar+n) 的特性,我們可以提供一個更“先進”的版本:

#define NUM_ELEMENTS(x) (sizeof((x)) / sizeof(0[(x)]))

這種下標索引的可交換性,只對內建的下標索引操作符有效,這一限制可以被用于約束NUM_ELEMENTS宏只對數組/指針有效,而拒絕重載了下標索引操作符的類類型。

 

接下來,我們來看NUM_ELEMENTS的實際運用:

int ar[10];
cout << NUM_ELEMENTS(ar) << endl;        // 毋庸置疑,輸出為10。
...
void fn(int ar[10])
{
    cout << NUM_ELEMENTS(ar) << endl;    // 本行結果呢?如果你說10,那么將會使你大失所望,
}                                        // 事實上,程序輸出為1。

看起來一切都井井有條,問題究竟出在哪里呢?呃,是這樣的,在C/C++中你無法將數組傳給函數!在C里面,數組被傳遞函數時總是被轉換成指針,非常干脆地阻止了你獲取數組大小的企圖。而C++基于兼容性的考慮,亦是如此。

所以先前給出的NUM_ELEMENTS宏定義依賴于預處理器進行的文本替換,因而存在一個嚴重的缺陷:如果我們(不小心)將它應用到一個指針身上,文本替換出來的結果將是錯誤的。

 

幸運的是,我們可以利用大多數現代編譯器都支持的一個特性來將數組和指針區別對待,那就是:從數組到指針的轉換(退化)在引用類型的模板實參決議中并不會發生。因而我們可以重新定義NUM_ELEMENTS宏為:

template<int N>
struct array_size_struct
{
    byte_t c[N];
};
 
template<typename T, int N>
array_size_struct<N> static_array_size_fn(T(&)[N]);
 
#define NUM_ELEMENTS(x) sizeof(static_array_size_fn(x).c)

其基本原理是:聲明(但不定義)一個模板函數,它接受一個元素類型為T、大小為N的數組的引用。這樣一來,指針類型以及用戶自定義類型就被拒之門外了(編譯報錯)。并且由于C++標準中,sizeof()的操作數不會被求值,所以我們無需定義static_array_size_fn()函數體,從而上述設施完全是零代價的。沒有任何運行時開銷,也不會導致代碼膨脹。

 

讓我們回到“C/C++數組在被傳遞給函數時會退化成指針”的問題上來,如果我們在現實中需要將一個將任意長度的數組傳遞給一個期望接受數組的函數,那么該怎么辦呢?困惑的實質在于數組的大小在傳遞過程中丟失了,因此,如果我們可以找到一種將數組大小隨之傳遞給函數的機制,問題就會迎刃而解。有了上面宏定義的經驗,通過模板我們找到一個解決方案:

template<typename T>
class array_proxy
{
public:
    typedef T               value_type;
    typedef array_proxy<T>  class_type;
    typedef value_type *    pointer;
    typedef value_type *    const_pointer;      // Non-const!
    typedef value_type &    reference;
    typedef value_type &    const_reference;    // Non-const!
    typedef size_t          size_type;
// 構造函數
public:
    template<size_t N>
    explicit array_proxy(T(&t)[N])    // 元素類型為T的數組
        : m_begin(&t[0])
        , m_end(&t[N])
    {}
    template<typename D, size_t N>
    explicit array_proxy(D(&d)[N])    // 元素類型為T兼容類型的數組
        : m_begin(&d[0])
        , m_end(&d[N])
    {
        constraint_must_be_same_size(T, D);    // 確保D和T大小相同
    }
    template<typename D>
    array_proxy(array_proxy<D> &d)
        : m_begin(d.begin())
        , m_end(d.end())
    {
        constraint_must_be_same_size(T, D);    // 確保D和T大小相同
    }
// 狀態
public:
    pointer             base();
    const_pointer       base() const;
    size_type           size() const;
    bool                empty() const;
    static size_type    max_size();
// 下標索引操作符
public:
    reference        operator [](size_t index);
    const_reference  operator [](size_t index) const;
// 迭代操作
public:
    pointer          begin();
    const_pointer    begin() const;
    pointer          end();
    const_pointer    end() const;
// 數據成員
private:
    pointer const m_begin;
    pointer const m_end;
// 聲明但不予實現
private:
    array_proxy & operator =(array_proxy const &);
};
 
// 轉發函數
template<typename T, size_t N>
inline array_proxy<T> make_array_proxy(T(&t)[N])
{
    return array_proxy<T>(t);
}
template<typename T>
inline array_proxy<T> make_array_proxy(T * base, size_t size)
{
    return array_proxy<T>(base, size);
}

客戶代碼修改為:

void process_array(const array_proxy<int> & ar)
{
    std::copy(ar.begin(), ar.end(), ostream_iterator<int>(cout, " "));
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    int ar[5] = {0, 1, 2, 3, 4};
    
    process_array(make_array_proxy(ar));
 
    return 0;
}

我們的問題終于有了一個徹底的解決方案。該解決方案是高效的(在任何一個說得過去的編譯器上它都沒有任何額外的開銷),是類型安全的,并且完全使得函數的設計者能夠防止潛在的誤用(更確切的說,讓代碼能夠更強的抵御派生類數組的誤用)。此外,它還足夠智能,允許派生類跟父類具有相同大小的情況下,它們的數組被“代理”。

最后一個優點是現在再也不可能將錯誤的數組長度傳給被調函數了,以前我們慣用的使用兩個參數(一個傳遞數組指針,一個傳遞數組長度)的函數版本中誤傳長度的危險是時時存在的。這個優勢使我們得以遵從DRY(Don't Repeat Yourself!)原則。

 

NULL宏

在C語言中,void*類型可以被隱式地轉換為其他任何指針類型,所以我們可以將NULL定義為((void*)0),從而跟其他任何指針類型間實現互相轉換。然而,C++不允許從void*到任何指針的隱式轉換,又因為C++中0可以被轉換為任何指針類型,因此,C++標準規定:NULL宏是一個由實現定義的C++空指針常量....其可能的定義方式包括0和0L,但絕對不是(void*)0。

由于0不可置疑的可以轉換成任何整型,甚至wchar_t和bool,以及浮點類型,這就意味著,使用NULL的時候,類型檢查將不再發生,我們很容易毫無察覺的走向厄運的深淵,連個警告都沒有??紤]如下情況:

// 自定義的字符串類
//
class String
{
    explicit String(char const *s);                  // 接受外界傳入的空指針
    explicit String(int cch, char chInit = '\0');    // 根據可能被使用的字符數估計,來初始化底層存儲
};

現在當我們用NULL做參數構造String時,第二個構造函數會被調用!也許和你的初衷不同,編譯器卻會一聲不吭的編譯通過。這可不妙。如果你將int改為size_t(或short、或long、或任何不是int的內建類型),編譯器將會在兩個轉換之間左右為難,結果是得到一個二義性錯誤。

 

我們想要個完善的空指針關鍵字!很快作者想出了辦法,你不應該感到驚訝,解決方案離不開模板:

struct NULL_v
{
// 構造函數
public:
    NULL_v()
    {}
// 轉換操作符
public:
    template<typename T>
    operator T* () const
    {
        return 0;
    }
    template<typename T2, typename C>
    operator T2 C::*() const
    {
        return 0;
    }
    template<typename T>
    bool equals(T const & rhs) const
    {
        return rhs == 0;
    }
// 聲明但不予實現
private:
    void operator &() const;    // Scott: 純粹是值的東西的地址是沒有任何意義的。
    NULL_v(NULL_v const &);
    NULL_v& operator =(NULL_v const &);
};
 
template<typename T>
inline bool operator ==(NULL_v const & lhs, T const & rhs)
{
    return lhs.equals(rhs);
}
 
template<typename T>
inline bool operator ==(T const & lhs, NULL_v const & rhs)
{
    return rhs.equals(lhs);
}
 
template<typename T>
inline bool operator !=(NULL_v const & lhs, T const & rhs)
{
    return !lhs.equals(rhs);
}
 
template<typename T>
inline bool operator !=(T const & lhs, NULL_v const & rhs)
{
    return !rhs.equals(lhs);
}
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美日本在线观看| 99国产精品国产精品久久| 国产精品久久久久久久久免费樱桃 | 欧美成人综合一区| 国产精品久久久久毛片大屁完整版| 久久字幕精品一区| 亚洲欧美日韩在线综合| 一本色道久久88亚洲综合88| 亚洲青色在线| 欧美激情女人20p| 亚洲国产精品成人va在线观看| 免费在线看成人av| 麻豆国产精品va在线观看不卡| 久久国产精品第一页| 亚洲免费中文| 亚洲欧美精品suv| 亚洲综合好骚| 欧美一区二区视频免费观看| 亚洲欧美国产va在线影院| 亚洲午夜国产一区99re久久| 亚洲综合视频1区| 亚洲日本中文字幕| 亚洲精品一区二| 亚洲乱码久久| 一区二区三区免费在线观看| 中文日韩在线视频| 午夜精品久久久久久久99黑人| 黄色成人在线网站| 亚洲国产三级网| 亚洲视频在线观看网站| 亚洲欧洲一区二区天堂久久 | 午夜亚洲激情| 亚洲毛片av| 性娇小13――14欧美| 久久综合伊人77777麻豆| 欧美日韩精品国产| 在线日韩一区二区| 亚洲精品三级| 久久夜色撩人精品| 亚洲免费精彩视频| 久久国产免费看| 欧美日韩国产综合视频在线观看中文| 国产伦精品一区二区三区免费迷| 亚洲第一精品久久忘忧草社区| 亚洲综合电影| 亚洲乱码视频| 欧美大片91| 亚洲国产成人av在线| 久久精品一区二区三区四区| 99国产精品久久久久久久久久| 免费av成人在线| 精品不卡视频| 久久国产精品一区二区| 亚洲美女视频在线免费观看| 欧美粗暴jizz性欧美20| 国产专区欧美专区| 久久精品二区三区| 午夜精品久久久久影视| 欧美性猛交一区二区三区精品| 夜夜精品视频一区二区| 亚洲国产精品久久| 久久综合激情| 在线日韩中文| 欧美成人国产| 牛人盗摄一区二区三区视频| 精品盗摄一区二区三区| 久久午夜羞羞影院免费观看| 欧美伊人久久久久久久久影院| 国产精品一区二区三区乱码 | 亚洲专区在线| 国产精品二区在线| 亚洲欧美另类久久久精品2019| 亚洲韩国精品一区| 欧美日韩国产精品一区| 亚洲视频狠狠| 亚洲一区二区在线免费观看视频 | 亚洲免费中文| 午夜一区在线| 国内一区二区三区在线视频| 欧美一站二站| 久久精品免费观看| 亚洲国产日韩欧美在线99| 亚洲国产一区二区三区青草影视 | 国产综合色在线视频区| 欧美韩国在线| 欧美日韩另类一区| 久久国产精品久久久| 久久中文在线| 亚洲视频精选| 欧美一区=区| 日韩视频免费观看高清在线视频| 亚洲午夜在线观看| 亚洲在线黄色| 亚洲高清不卡在线| 亚洲精品小视频| 国产日本亚洲高清| 欧美激情2020午夜免费观看| 国产精品国产三级国产普通话三级| 久久久精品免费视频| 久久久久久久性| 亚洲一区在线直播| 久久久一区二区三区| 一区二区三区视频免费在线观看| 欧美一区二区三区成人| 一本色道久久综合一区| 欧美一区二区三区日韩视频| 亚洲精品一区在线观看| 久久av一区二区三区亚洲| 中文日韩电影网站| 久久久91精品国产一区二区三区 | 免费观看一区| 欧美日韩精品综合| 免播放器亚洲一区| 国产精品久久久久久久久借妻| 麻豆视频一区二区| 国产老肥熟一区二区三区| 亚洲欧洲一区二区三区在线观看| 国产亚洲精品久久久| 在线亚洲激情| 一区二区动漫| 欧美精品黄色| 欧美第一黄色网| 一区二区三区在线视频免费观看| 亚洲专区国产精品| 亚洲香蕉伊综合在人在线视看| 久久综合狠狠| 欧美大成色www永久网站婷| 在线观看亚洲专区| 亚洲午夜久久久久久久久电影院| 欧美日韩视频不卡| 亚洲电影在线免费观看| 精品动漫一区二区| 久久精品亚洲精品| 久久亚洲精品视频| 国产一区在线看| 欧美一站二站| 久久久99久久精品女同性| 国产欧美午夜| 西瓜成人精品人成网站| 久久精品国产第一区二区三区最新章节 | 欧美一区二区视频在线观看| 国产精品户外野外| 亚洲午夜精品| 午夜精品成人在线视频| 国产精品美女久久久久aⅴ国产馆| aa日韩免费精品视频一| 亚洲欧洲av一区二区三区久久| 欧美性事免费在线观看| 亚洲一二三区在线观看| 亚洲特级片在线| 国产日本欧美一区二区三区| 亚洲亚洲精品三区日韩精品在线视频| 亚洲午夜精品久久| 国产精品女人毛片| 欧美一区二区三区在线观看 | 91久久国产综合久久91精品网站| 亚洲黄色在线看| 欧美日本不卡| 亚洲资源av| 欧美大片91| 亚洲香蕉成视频在线观看| 国产精品蜜臀在线观看| 久久精品午夜| 亚洲精品老司机| 欧美一区二区精品| 亚洲福利视频一区二区| 欧美日韩一区二区三区在线| 香蕉国产精品偷在线观看不卡| 你懂的网址国产 欧美| 亚洲视频在线观看| 加勒比av一区二区| 欧美母乳在线| 欧美一区二区三区免费视频| 亚洲二区在线| 久久成人国产| 亚洲伦理精品| 国产丝袜一区二区| 欧美精品系列| 久久精品免费| 亚洲午夜视频在线| 亚洲国产成人久久综合一区| 欧美在线二区| 国产精品99久久久久久有的能看| 红桃视频成人| 国产美女诱惑一区二区| 欧美精品一区在线| 亚洲国产日日夜夜| 尤物精品在线| 欧美日韩在线观看一区二区三区 | 欧美黑人在线观看| 午夜精品久久久久久99热| 亚洲精品乱码久久久久久久久| 欧美专区亚洲专区| 中文精品在线| 亚洲六月丁香色婷婷综合久久| 国产亚洲精品久久飘花| 欧美日韩在线大尺度| 欧美国产精品日韩| 久久美女艺术照精彩视频福利播放| 亚洲视频一区在线|