• <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>
            posts - 13, comments - 4, trackbacks - 0, articles - 0
              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            Imperfect C++ 讀書(shū)筆記(三)

            Posted on 2008-11-20 00:28 Batiliu 閱讀(386) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): 讀書(shū)筆記

            數(shù)組和指針

            首先我們來(lái)看一個(gè)經(jīng)典的C/C++求數(shù)組元素個(gè)數(shù)的解決方案:

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

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

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

            這種下標(biāo)索引的可交換性,只對(duì)內(nèi)建的下標(biāo)索引操作符有效,這一限制可以被用于約束NUM_ELEMENTS宏只對(duì)數(shù)組/指針有效,而拒絕重載了下標(biāo)索引操作符的類(lèi)類(lèi)型。

             

            接下來(lái),我們來(lái)看NUM_ELEMENTS的實(shí)際運(yùn)用:

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

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

            所以先前給出的NUM_ELEMENTS宏定義依賴于預(yù)處理器進(jìn)行的文本替換,因而存在一個(gè)嚴(yán)重的缺陷:如果我們(不小心)將它應(yīng)用到一個(gè)指針身上,文本替換出來(lái)的結(jié)果將是錯(cuò)誤的。

             

            幸運(yùn)的是,我們可以利用大多數(shù)現(xiàn)代編譯器都支持的一個(gè)特性來(lái)將數(shù)組和指針區(qū)別對(duì)待,那就是:從數(shù)組到指針的轉(zhuǎn)換(退化)在引用類(lèi)型的模板實(shí)參決議中并不會(huì)發(fā)生。因而我們可以重新定義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)

            其基本原理是:聲明(但不定義)一個(gè)模板函數(shù),它接受一個(gè)元素類(lèi)型為T(mén)、大小為N的數(shù)組的引用。這樣一來(lái),指針類(lèi)型以及用戶自定義類(lèi)型就被拒之門(mén)外了(編譯報(bào)錯(cuò))。并且由于C++標(biāo)準(zhǔn)中,sizeof()的操作數(shù)不會(huì)被求值,所以我們無(wú)需定義static_array_size_fn()函數(shù)體,從而上述設(shè)施完全是零代價(jià)的。沒(méi)有任何運(yùn)行時(shí)開(kāi)銷(xiāo),也不會(huì)導(dǎo)致代碼膨脹。

             

            讓我們回到“C/C++數(shù)組在被傳遞給函數(shù)時(shí)會(huì)退化成指針”的問(wèn)題上來(lái),如果我們?cè)诂F(xiàn)實(shí)中需要將一個(gè)將任意長(zhǎng)度的數(shù)組傳遞給一個(gè)期望接受數(shù)組的函數(shù),那么該怎么辦呢?困惑的實(shí)質(zhì)在于數(shù)組的大小在傳遞過(guò)程中丟失了,因此,如果我們可以找到一種將數(shù)組大小隨之傳遞給函數(shù)的機(jī)制,問(wèn)題就會(huì)迎刃而解。有了上面宏定義的經(jīng)驗(yàn),通過(guò)模板我們找到一個(gè)解決方案:

            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;
            // 構(gòu)造函數(shù)
            public:
                template<size_t N>
                explicit array_proxy(T(&t)[N])    // 元素類(lèi)型為T(mén)的數(shù)組
                    : m_begin(&t[0])
                    , m_end(&t[N])
                {}
                template<typename D, size_t N>
                explicit array_proxy(D(&d)[N])    // 元素類(lèi)型為T(mén)兼容類(lèi)型的數(shù)組
                    : 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大小相同
                }
            // 狀態(tài)
            public:
                pointer             base();
                const_pointer       base() const;
                size_type           size() const;
                bool                empty() const;
                static size_type    max_size();
            // 下標(biāo)索引操作符
            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;
            // 數(shù)據(jù)成員
            private:
                pointer const m_begin;
                pointer const m_end;
            // 聲明但不予實(shí)現(xiàn)
            private:
                array_proxy & operator =(array_proxy const &);
            };
             
            // 轉(zhuǎn)發(fā)函數(shù)
            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;
            }

            我們的問(wèn)題終于有了一個(gè)徹底的解決方案。該解決方案是高效的(在任何一個(gè)說(shuō)得過(guò)去的編譯器上它都沒(méi)有任何額外的開(kāi)銷(xiāo)),是類(lèi)型安全的,并且完全使得函數(shù)的設(shè)計(jì)者能夠防止?jié)撛诘恼`用(更確切的說(shuō),讓代碼能夠更強(qiáng)的抵御派生類(lèi)數(shù)組的誤用)。此外,它還足夠智能,允許派生類(lèi)跟父類(lèi)具有相同大小的情況下,它們的數(shù)組被“代理”。

            最后一個(gè)優(yōu)點(diǎn)是現(xiàn)在再也不可能將錯(cuò)誤的數(shù)組長(zhǎng)度傳給被調(diào)函數(shù)了,以前我們慣用的使用兩個(gè)參數(shù)(一個(gè)傳遞數(shù)組指針,一個(gè)傳遞數(shù)組長(zhǎng)度)的函數(shù)版本中誤傳長(zhǎng)度的危險(xiǎn)是時(shí)時(shí)存在的。這個(gè)優(yōu)勢(shì)使我們得以遵從DRY(Don't Repeat Yourself!)原則。

             

            NULL宏

            在C語(yǔ)言中,void*類(lèi)型可以被隱式地轉(zhuǎn)換為其他任何指針類(lèi)型,所以我們可以將NULL定義為((void*)0),從而跟其他任何指針類(lèi)型間實(shí)現(xiàn)互相轉(zhuǎn)換。然而,C++不允許從void*到任何指針的隱式轉(zhuǎn)換,又因?yàn)镃++中0可以被轉(zhuǎn)換為任何指針類(lèi)型,因此,C++標(biāo)準(zhǔn)規(guī)定:NULL宏是一個(gè)由實(shí)現(xiàn)定義的C++空指針常量....其可能的定義方式包括0和0L,但絕對(duì)不是(void*)0。

            由于0不可置疑的可以轉(zhuǎn)換成任何整型,甚至wchar_t和bool,以及浮點(diǎn)類(lèi)型,這就意味著,使用NULL的時(shí)候,類(lèi)型檢查將不再發(fā)生,我們很容易毫無(wú)察覺(jué)的走向厄運(yùn)的深淵,連個(gè)警告都沒(méi)有。考慮如下情況:

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

            現(xiàn)在當(dāng)我們用NULL做參數(shù)構(gòu)造String時(shí),第二個(gè)構(gòu)造函數(shù)會(huì)被調(diào)用!也許和你的初衷不同,編譯器卻會(huì)一聲不吭的編譯通過(guò)。這可不妙。如果你將int改為size_t(或short、或long、或任何不是int的內(nèi)建類(lèi)型),編譯器將會(huì)在兩個(gè)轉(zhuǎn)換之間左右為難,結(jié)果是得到一個(gè)二義性錯(cuò)誤。

             

            我們想要個(gè)完善的空指針關(guān)鍵字!很快作者想出了辦法,你不應(yīng)該感到驚訝,解決方案離不開(kāi)模板:

            struct NULL_v
            {
            // 構(gòu)造函數(shù)
            public:
                NULL_v()
                {}
            // 轉(zhuǎn)換操作符
            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;
                }
            // 聲明但不予實(shí)現(xiàn)
            private:
                void operator &() const;    // Scott: 純粹是值的東西的地址是沒(méi)有任何意義的。
                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);
            }
            国产精品成人久久久久久久| 久久天天躁夜夜躁狠狠| 精品久久久久久久| 亚洲午夜无码AV毛片久久| 欧美日韩精品久久久免费观看 | 爱做久久久久久| 午夜精品久久久久成人| 国产午夜免费高清久久影院| 国产亚洲色婷婷久久99精品91| 久久精品人人做人人爽电影| 国产精品久久久久AV福利动漫| 久久青青草原精品国产不卡| 婷婷久久综合九色综合98| 久久久久久久免费视频| 狠狠色噜噜狠狠狠狠狠色综合久久 | 青青国产成人久久91网| 欧美伊人久久大香线蕉综合| 久久99热这里只有精品国产| 久久久久久无码Av成人影院| AV无码久久久久不卡蜜桃| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 亚洲日韩欧美一区久久久久我 | 婷婷久久精品国产| 91亚洲国产成人久久精品网址| 亚洲色婷婷综合久久| 99久久综合国产精品免费| 久久乐国产精品亚洲综合| 99久久精品国产一区二区蜜芽| 国产精品女同久久久久电影院| 亚洲日韩中文无码久久| 久久久久久久波多野结衣高潮| 欧美成a人片免费看久久| 精品综合久久久久久88小说 | 99久久免费国产特黄| 美女写真久久影院| 久久婷婷久久一区二区三区| 2022年国产精品久久久久| 国产一区二区精品久久| 日韩欧美亚洲综合久久影院d3| 热久久这里只有精品| 久久婷婷五月综合色99啪ak|