• <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)系 :: 聚合  :: 管理

            數(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)看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)換(退化)在引用類型的模板實(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è)元素類型為T(mén)、大小為N的數(shù)組的引用。這樣一來(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)銷,也不會(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])    // 元素類型為T(mén)的數(shù)組
                    : m_begin(&t[0])
                    , m_end(&t[N])
                {}
                template<typename D, size_t N>
                explicit array_proxy(D(&d)[N])    // 元素類型為T(mén)兼容類型的數(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)銷),是類型安全的,并且完全使得函數(shù)的設(shè)計(jì)者能夠防止?jié)撛诘恼`用(更確切的說(shuō),讓代碼能夠更強(qiáng)的抵御派生類數(shù)組的誤用)。此外,它還足夠智能,允許派生類跟父類具有相同大小的情況下,它們的數(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*類型可以被隱式地轉(zhuǎn)換為其他任何指針類型,所以我們可以將NULL定義為((void*)0),從而跟其他任何指針類型間實(shí)現(xiàn)互相轉(zhuǎn)換。然而,C++不允許從void*到任何指針的隱式轉(zhuǎn)換,又因?yàn)镃++中0可以被轉(zhuǎn)換為任何指針類型,因此,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)類型,這就意味著,使用NULL的時(shí)候,類型檢查將不再發(fā)生,我們很容易毫無(wú)察覺(jué)的走向厄運(yùn)的深淵,連個(gè)警告都沒(mé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)建類型),編譯器將會(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);
            }
            久久亚洲私人国产精品| 久久综合色之久久综合| 久久无码国产| 2021久久国自产拍精品| 国内精品九九久久久精品| 久久精品国内一区二区三区| 夜夜亚洲天天久久| 国产一区二区精品久久岳| 久久国内免费视频| 久久亚洲欧美国产精品| 国产美女久久精品香蕉69| 97精品依人久久久大香线蕉97 | 伊人久久一区二区三区无码| 久久婷婷五月综合97色 | 久久人人爽人人爽人人片AV不| 综合久久精品色| 亚洲香蕉网久久综合影视 | 久久人人爽人人爽人人片AV高清| 婷婷五月深深久久精品| 国产综合免费精品久久久| 久久国产精品国语对白| 日本加勒比久久精品| 国产精品久久久福利| 国产高清国内精品福利99久久| 久久精品久久久久观看99水蜜桃| 无码国内精品久久人妻蜜桃| 国产精品久久网| 欧美一级久久久久久久大片| 日韩欧美亚洲综合久久| 亚洲一区二区三区日本久久九| 久久精品国产99久久香蕉| 久久综合欧美成人| 久久婷婷成人综合色综合| 韩国无遮挡三级久久| 亚洲国产精品综合久久网络| 久久精品无码午夜福利理论片| 久久亚洲精品无码aⅴ大香| 精品无码久久久久国产| 久久人人爽人人澡人人高潮AV| 久久精品国产亚洲av麻豆蜜芽 | 久久综合久久综合九色|