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

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>
            国产欧美亚洲视频| 欧美一区二区三区另类| 欧美日韩一区视频| 免费不卡亚洲欧美| 久久激情五月婷婷| 午夜久久一区| 欧美在线999| 久久久999精品| 久久久女女女女999久久| 久久精品中文| 久久se精品一区精品二区| 国产精品免费看| 一区二区欧美日韩| 亚洲欧美制服另类日韩| 亚洲一级网站| 欧美一区二区视频在线观看| 黑人一区二区| 一区视频在线播放| 亚洲黑丝在线| 亚洲一区二区3| 欧美在线观看一区二区| 久久久99精品免费观看不卡| 欧美精品免费在线| 亚洲精品日韩一| 中文国产成人精品久久一| 9i看片成人免费高清| 久久丁香综合五月国产三级网站| 国产精品卡一卡二| 精品999在线观看| 日韩视频在线观看免费| 一区二区三区|亚洲午夜| 久久米奇亚洲| 久久一区二区三区国产精品 | 亚洲在线视频观看| 亚洲电影在线看| 亚洲国产天堂网精品网站| 亚洲一区激情| 小黄鸭视频精品导航| 久久成人精品无人区| 国产精品久久久久一区二区| 久久精品欧洲| 欧美激情第8页| 国产日韩精品一区二区三区在线| 欧美激情网友自拍| 国产九九视频一区二区三区| 亚洲电影免费观看高清完整版在线 | 欧美色图五月天| 国产亚洲aⅴaaaaaa毛片| 亚洲激情一区| 麻豆成人小视频| 先锋影音一区二区三区| 国产精品乱码人人做人人爱| 欧美日韩亚洲在线| 亚洲黄色一区二区三区| 久久人人看视频| 欧美在线视频在线播放完整版免费观看| 欧美一级久久久| 国产精品大全| 亚洲欧美日韩国产中文| 欧美激情1区2区3区| 国产午夜精品理论片a级探花 | 久久国产精品色婷婷| 欧美午夜一区二区福利视频| 夜夜嗨网站十八久久| 在线视频欧美一区| 国产精品视频xxxx| 亚洲伊人网站| 亚洲在线播放| 久久精品男女| 国产日韩精品久久久| 小辣椒精品导航| 亚洲欧美不卡| 国产日韩欧美三级| 日韩手机在线导航| 女人香蕉久久**毛片精品| 国产精品九九| 亚洲尤物影院| 91久久精品一区| 午夜一区二区三区在线观看| 国产精品三级久久久久久电影| 欧美性猛交99久久久久99按摩 | 日韩视频在线一区| 欧美激情性爽国产精品17p| 香蕉久久夜色精品国产| 国产精品自在线| 欧美影院久久久| 性色一区二区三区| 国产亚洲欧洲一区高清在线观看| 激情小说另类小说亚洲欧美| 亚洲欧美一级二级三级| 亚洲午夜电影网| 国产精品系列在线| 国产一区二区三区久久 | 亚洲一区二区在线| 欧美日韩精品免费看| 欧美日韩二区三区| 夜夜嗨av一区二区三区四区| 在线亚洲观看| 久久精品亚洲一区二区三区浴池| 亚洲欧美日韩精品综合在线观看| 欧美一区二区三区免费观看视频| 亚洲欧美怡红院| 欧美中文字幕| 精品动漫av| 亚洲精品久久在线| 米奇777在线欧美播放| 午夜精品一区二区三区在线视| 欧美一区免费视频| 欧美一区二区国产| 亚洲高清免费视频| 亚洲永久精品大片| 亚洲欧洲精品一区二区精品久久久| 亚洲在线1234| 亚洲在线观看视频| 久久九九热re6这里有精品| 欧美不卡在线视频| 一区二区三区黄色| 亚洲女人av| 狠狠色丁香婷综合久久| 99xxxx成人网| 激情欧美一区| 亚洲精选91| 欧美日韩三级视频| 亚洲激情黄色| 国产亚洲欧美日韩日本| 国产一区二区三区日韩| 日韩一级精品视频在线观看| 久久成人18免费网站| 亚洲美女精品久久| 欧美一区二区三区视频免费播放| 欧美紧缚bdsm在线视频| 亚洲综合视频1区| 欧美劲爆第一页| 亚洲高清久久久| 在线午夜精品| 亚洲国产欧美一区二区三区同亚洲 | 男女激情视频一区| 亚洲视频视频在线| 噜噜噜91成人网| 欧美在线播放| 欧美日韩精品免费看| 亚洲精品久久久久久一区二区| 国产一区视频在线看| 欧美插天视频在线播放| 国产主播一区二区三区| 日韩写真在线| 亚洲激情国产精品| 久久久久一区二区三区四区| 免费久久99精品国产自| 欧美在线国产精品| 亚洲天堂免费观看| 日韩五码在线| 亚洲国产一区二区在线| 亚洲天堂成人在线观看| 亚洲欧美一区在线| 欧美精品成人| 免费观看国产成人| 国内不卡一区二区三区| 美女国产精品| 亚洲成人在线网| 91久久久久久久久| 亚洲精华国产欧美| 亚洲电影免费在线观看| 欧美日本在线看| 亚洲人成在线免费观看| 欧美日韩视频在线一区二区| 亚洲国产欧美一区二区三区久久| 欧美精品在线观看一区二区| 91久久在线视频| 蜜臀av在线播放一区二区三区| 国内精品国语自产拍在线观看| 久久天天狠狠| 激情久久五月天| 美日韩在线观看| 亚洲无限av看| 久久亚洲私人国产精品va媚药| 久久久久久久999| 在线观看中文字幕亚洲| 欧美在线视频网站| 久久亚洲欧美| 亚洲精品久久久久久下一站| 欧美日韩国产欧美日美国产精品| 亚洲女同同性videoxma| 欧美四级剧情无删版影片| 久久本道综合色狠狠五月| 女仆av观看一区| 欧美影院午夜播放| 欧美一区二区高清| 欧美日韩亚洲系列| 欧美一区二区在线视频| 久久最新视频| 亚洲日本免费| 夜夜爽夜夜爽精品视频| 欧美一区日本一区韩国一区| 伊人久久av导航| 欧美激情精品久久久久久黑人| 欧美一乱一性一交一视频| 国产日韩亚洲欧美精品| 欧美日韩中文精品| 午夜精品久久久久久久99水蜜桃|