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

Shuffy

不斷的學(xué)習(xí),不斷的思考,才能不斷的進(jìn)步.Let's do better together!
posts - 102, comments - 43, trackbacks - 0, articles - 19

【轉(zhuǎn)】http://m.shnenglu.com/tiandejian/archive/2007/08/02/ec_25.html
第25條:     最好不要讓交換數(shù)值函數(shù) swap 拋出異常

swap 是一個非常有趣的程序。它最早是作為 STL 的一部分引入 C++ 的, 而后就成為了異常安全編程的主體內(nèi)容(參見第 29 條),另外對于可以自賦值的對象而言它還是一個常用的復(fù)制處理機制。由于 swap 如此神通廣大,那么以一個恰當(dāng)?shù)姆绞饺崿F(xiàn)它就顯得十分重要了,但是它的舉足輕重的地位也決定了實現(xiàn)它并不是一件手到擒來的事情。在本小節(jié)中,我們就會針對 swap 函數(shù)展開探索,逐步掌握如何去駕馭它。

swap 函數(shù)的功能是交換兩個對象的值。在默認(rèn)情況下,交換工作時通過標(biāo)準(zhǔn)的 swap 函數(shù)完成的。它的標(biāo)準(zhǔn)實現(xiàn)方式就能完美地完成你所期望的工作:

namespace std {

 

 template<typename T>          // std::swap 的標(biāo)準(zhǔn)實現(xiàn)

 void swap(T& a, T& b)         // 交換 a b 的值

 {

    T temp(a);

    a = b;

    b = temp;

 }

 

}

只要你的類型支持復(fù)制(通過拷貝構(gòu)造函數(shù)和拷貝復(fù)制運算符),那么默認(rèn)的 swap 實現(xiàn)就可以讓你的類型的兩個對象互相交換,你不需要做任何具體的工作來支持這一功能。

然而,你可能對默認(rèn)的 swap 實現(xiàn)保有諸多不滿。它會帶來 3 次對象復(fù)制工作: a 復(fù)制到 temp b a temp b 。對于一些類型來說,這些復(fù)制操作并不都是必需的。對于這些類型來說,默認(rèn)的 swap 會成為你程序的桎梏。

上述的那種類型大都符合下面的特征:它的主要成分是一個指針,這一指針會指向另一個類型,真實的數(shù)據(jù)包含在這另一個類型中。對這一設(shè)計方式的一種常見的稱謂是“ pimpl idiom ”( pointer to implementation ,指向?qū)崿F(xiàn)的指針,參見第 31 )。比如 Widget 類可以使用這種設(shè)計模式。請看下面的代碼:

class WidgetImpl {                    // 保存 Widget 的數(shù)據(jù)的類

public:                               // 細(xì)節(jié)不重要

 ...

 

private:

 int a, b, c;                        // 可能會有很多數(shù)據(jù)

 std::vector<double> v;              // 復(fù)制它們的代價是很高的!

 ...

};

 

class Widget {                        // 使用 pimpl idiom 的類

public:

 Widget(const Widget& rhs);

 

 Widget& operator=(const Widget& rhs) // 要復(fù)制一個 Widget 對象,只要

 {                                   // 復(fù)制對應(yīng)的 WidgetImpl 對象。

   ...                                // 關(guān)于 operator= 實現(xiàn)的一般信息

   *pImpl = *(rhs.pImpl);             // 參見第 10 11 12

   ...

 }

 

 ...

 

private:

 WidgetImpl *pImpl;                  // ptr to object with this

};                                    // Widget's data

為了交換兩個 Widget 對象的值,我們所要做的僅僅是交換他們的 pImpl 指針,但是默認(rèn)的 swap 算法是不可能知道這一切的,它不僅會復(fù)制三個 Widget 對象,同時也會復(fù)制三個 Widget 對象。這樣做效率太低了。

我們要做的是告訴 std::swap 當(dāng)交換 Widget 時,執(zhí)行的交換操作應(yīng)當(dāng)僅僅針對它們內(nèi)部的 pImpl 指針。有一種精確的說法來描述這一方法:將 Widget 特化。下面是基本的思想,盡管以這種方式不能通過編譯:

 

namespace std {

 

 template<>                      // T Widget 時,

 void swap<Widget>(Widget& a,   // 這是 std::swap 的一個特化版本

                    Widget& b)     // 這段代碼不能通過編譯

 {

    swap(a.pImpl, b.pImpl);      // 要交換兩個 Widget

 }                              // 只需要交換它們的 pImpl 指針

}

程序開端的“ template<> ”告訴我們這是 std::swap 的一個完全特化模板,函數(shù)名后面的“ <Widget> ”告訴我們 T 被特化為了 Widget 。換種說法,當(dāng)通用的 swap 模板應(yīng)用于 Widget 時,應(yīng)當(dāng)使用這一具體實現(xiàn)。一般情況下,我們沒有權(quán)限去改動 std 名字空間內(nèi)部的內(nèi)容,但是我們有權(quán)針對我們自己創(chuàng)建的類型(比如 Widget )來完整地特化標(biāo)準(zhǔn)模板(就像 swap )。這就是我們所要做的。

然而,就像我說過的,這段代碼是不能通過編譯的。這是因為它嘗試訪問 a b 內(nèi)部的 pImpl 指針,但是它們是私有的。我們可以將我們的特化函數(shù)聲明為友元,但慣例是不一樣的:慣例要求我們讓 Widget 包含一個名為 swap 的公共成員函數(shù),讓這個 swap 進(jìn)行實際的交換工作美然后特化 std:swap 來調(diào)用這一成員函數(shù)。

class Widget {                        // 同上,

public:                               // 僅添加了一個 swap 成員函數(shù)

 ...

 void swap(Widget& other)

 {

    using std::swap;                  // 本節(jié)后面會解釋為什么這樣聲明

 

    swap(pImpl, other.pImpl);         // 交換 pImpl 指針來交換 Widget

 }

 ...

};

 

namespace std {

 

 template<>                       // 特化的 std::swap (已修正)

 void swap<Widget>(Widget& a, Widget& b)

 {

    a.swap(b);                        // 要交換 Widget

 }                                   // 只要調(diào)用它們的 swap 成員函數(shù)

 

}

這樣的代碼不僅僅可以通過編譯,而且也與 STL 容器相協(xié)調(diào),它不僅僅提供了公有的 swap 成員函數(shù),而且還提供了特化的 std::swap 來調(diào)用這些成員函數(shù)。

然而,我們不難發(fā)現(xiàn), Widget WidgetImpl 都是類模板,而不是類,似乎我們可以自定義 WidgetImpl 中保存的數(shù)據(jù)的類型:

template<typename T>

class WidgetImpl { ... };

 

template<typename T>

class Widget { ... };

將一個 swap 成員函數(shù)放入 Widget 中(如果需要,也可以是 WidgetImpl )仍然十分簡單,但是我們對 std::swap 特化時將會遇到問題。下面是我們希望編寫的代碼:

namespace std {

 template<typename T>

 void swap<Widget<T> >(Widget<T>& a, Widget<T>& b)

                                // 錯誤!非法代碼

 { a.swap(b); }

 

}

這樣的代碼看上去完美無瑕,但是它是非法的。因為其中嘗試對一個函數(shù)模板( std::swap )進(jìn)行不完全的特化,但是,盡管 C++ 允許對類模板進(jìn)行不完全特化,但是函數(shù)模板就不行了。這一代碼將不能通過編譯(盡管一些編譯器會錯誤的接受)。

當(dāng)你期望對一個函數(shù)模板進(jìn)行“不完全特化”時,通常的做法非常簡單,就是添加一個該函數(shù)的重載。代碼可能是下面的樣子:

namespace std {

 

 template<typename T>           // std::swap 的一個重載

 void swap(Widget<T>& a, Widget<T>& b)

                                // (注意 swap 后邊沒有 <...>

 { a.swap(b); }                 // 下文解釋了為什么這樣做不合法

 

}

一般情況下,重載函數(shù)模板是可以的,但是 std 是一個很特殊的名字空間,它的規(guī)則也是獨特的。對 std 中的模板進(jìn)行完全特化是合法的,但是為 std 添加一個新的模板卻是不合法的(類或函數(shù)或其他一切都不可以)。 std 的內(nèi)容是由 C++ 標(biāo)準(zhǔn)化委員會一手確定的,我們無法修改他們所規(guī)定的任何形式,只能“望代碼興嘆”。越軌的代碼似乎可以運行,但它們的行為確實不可預(yù)知的。如果你希望你的代碼擁有可預(yù)知的行為,你就不應(yīng)該寄希望于在 std 中添加新的內(nèi)容。

那么應(yīng)該怎么辦呢?我們?nèi)匀恍枰环N方法來讓其他人通過調(diào)用 swap 來訪問我們更加高效的特化版本。答案很簡單。我們?nèi)匀豢梢酝ㄟ^聲明一個非成員函數(shù) swap 來調(diào)用成員函數(shù) swap 實現(xiàn),只要這個非成員函數(shù)不是 std::swap 的特化或者重載版本即可。比如說,如果我們所有與 Widget 相關(guān)的功能都在名字空間 WidgetStuff 中,那么代碼看上去應(yīng)該是這樣:

namespace WidgetStuff {

 ...                            // 模板化的 WidgetImpl ,等等

 

 template<typename T>           // 同上,包括 swap 成員函數(shù)

 class Widget { ... };

 ...

 

 template<typename T>            // 非成員函數(shù) swap

   void swap(Widget<T>& a, Widget<T>& b)

                                // 不屬于 std 名字空間

 {

    a.swap(b);

 }

}

現(xiàn)在,如果任意位置的代碼對兩個 Widget 對象調(diào)用了 swap C++ 的名字搜尋守則(更具體地說,就是所謂的參數(shù)依賴搜尋或 Koenig 搜尋)將會在 WidgetStuff 中查找具體到 Widget 的版本。這恰恰是我們需要的。

由于這種方法針對類或者類模板可以正常運行,所以看上去似乎我們在任何情況下都使用它。但是遺憾的是,我們還是要對于類的 std::swap 進(jìn)行特化(稍后會交代理由),所以如果你想要在盡可能多的上下文中(你所需要的)調(diào)用具體到類的 swap 版本,你就需要在你的類所在的名字空間編寫一個非成員版本的 swap ,同時還需要一個 std::swap 的特化版本。

順便說一下,即使你沒有使用名字空間,上述內(nèi)容仍然有效(也就是說,你仍需要一個非成員的 swap 去調(diào)用成員函數(shù) swap ),但是為什么你要把所有的類、模板、函數(shù)、枚舉類型、 enumerant typedef 的名字統(tǒng)統(tǒng)塞進(jìn)全局名字空間里呢?如果你對編程規(guī)范有一點概念的話,都不會這樣做的。

到目前為止我所介紹的一切內(nèi)容都是以 swap 的作者的角度展開的,但是以一個客戶端程序員的眼光來審視一下 swap 也是很有價值的。假設(shè)你正在編寫一個函數(shù)模板,這里你需要交換兩個對象的值:

template<typename T>

void doSomething(T& obj1, T& obj2)

{

 ...

 swap(obj1, obj2);

 ...

}

這里應(yīng)該調(diào)用哪一個 swap 呢? std 中存在一個通用版本,這是你所知道的;另外 std 中可能還有一個針對這一通用版本的特化版本,它可能存在也可能不存在;或者一個模板的版本,它可能存在也可能不存在,它是否在一個名字空間中也不能確定(但可以肯定不在 std 名字空間中)?此時你所希望的是,如果存在一個模板版本的話,就調(diào)用它;如果不存在,就返回調(diào)用 std 中的通用版本。以下是滿足這一要求的代碼:

template<typename T>

void doSomething(T& obj1, T& obj2)

{

 using std::swap;               // 確保 std::swap 在此函數(shù)中可用

 ...

 swap(obj1, obj2);              // 為類型 T 的對象調(diào)用最佳的 swap

 ...

}

當(dāng)編譯器看到對 swap 的調(diào)用時,它們會尋找恰當(dāng)?shù)?/span> swap 來進(jìn)行調(diào)用。 C++ 的名字搜尋原則確保了在全局或 T 類型所在的名字空間中來查找所有的精確到 T swap 。(舉例說,如果 T 是位于 WidgetStuff 名字空間中的 Widget ,那么編譯器將會使用參數(shù)依賴搜尋方式來查找 WidgetStuff 中的 swap 。)如果沒有精確到 T swap 存在,那么編譯器將會使用 std 中的 swap ,多虧了 using 聲明可以使 std::swap 在本函數(shù)中可見。然而即使這樣,編譯器也更期望得到一個精確到 T std::swap 的特化版本,而不是未確定類型的模板,因此如果 std::swap 特化為 T 版本,那么這一特化的版本將會得到使用。

因此,調(diào)用正確的 swap 十分簡單。你所需要關(guān)心的事僅僅是不去限制對它的調(diào)用,因為如果這樣做會使 C++ 如何決定去調(diào)用函數(shù)的方式受到影響。舉例說,如果你用下面的方式調(diào)用了 swap

std::swap(obj1, obj2);          // 調(diào)用 swap 的錯誤方法

你強迫編譯器僅僅去考慮 std 中的 swap (包括所有的模板特化版本),這樣做就排除了得到一個位于其他位置的精確到 T 版本的 swap 的可能,即使它是更加合理的。然而,一些進(jìn)入誤區(qū)的程序員還是會以這種方式限制 swap 的調(diào)用,這里你就可以看出,為你的類提供一個 std::swap 的完全特化版本是多么重要:對于那些使用不恰當(dāng)?shù)木幋a風(fēng)格寫出的代碼(這樣的代碼也存在于一些標(biāo)準(zhǔn)庫的實現(xiàn)當(dāng)中,如果你感興趣可以自己編寫一些代碼,來幫助這樣的代碼盡可能的提高效率),精確到類的 swap 實現(xiàn)仍然有效。

此刻,我們已經(jīng)介紹了默認(rèn)的 swap 、成員 swap 、非成員 swap std::swap 的特化版本,以及對 swap 的調(diào)用,現(xiàn)在讓我們來做一個總結(jié)。

首先,如果對你的類或者類模板使用默認(rèn)的 swap 實現(xiàn)能夠得到可以接受的效率,你就不需要做任何事情。任何人想要交換你創(chuàng)建的類型的對象時,都會去調(diào)用默認(rèn)的版本,此時可以正常工作。

其次,如果默認(rèn)的 swap 實現(xiàn)并不夠高效(大多數(shù)情況下意味著你的類或模板正在運用 pimpl idiom ),請按下面步驟進(jìn)行:

1. 提供一個公用的 swap 成員函數(shù),讓它可以高效的交換你的類型的兩個對象的值。理由將在后面列出,這個函數(shù)永遠(yuǎn)不要拋出異常。

2. 在你的類或模板的同一個名字空間中提供一個非成員的 swap 。讓它調(diào)用你的 swap 成員函數(shù)。

3. 如果你正在編寫一個類(而不是類模板),要為你的類提供一個 std::swap 的特化版本。同樣讓它調(diào)用你的 swap 成員函數(shù)。

最后,如果你正在調(diào)用 swap ,要確保使用一條 using 聲明來使 std::swap 對你的函數(shù)可見,然后在調(diào)用 swap 時,不要做出任何名字空間的限制。

文中還有一處欠缺,那就是本文的標(biāo)題中的敬告:不要讓 swap 的成員函數(shù)版本拋出異常。這是因為 swap 最重要的用途之一就是幫助類(或類模板)來提供異常安全的保證。第 29 條中詳細(xì)介紹了這一點,但是這一技術(shù)做出了“ swap 的成員函數(shù)版本永遠(yuǎn)不會拋出異常”這一假設(shè)。這一約束僅僅應(yīng)用于成員函數(shù)版本,非成員版本則不受這一限制。這是因為 swap 的默認(rèn)版本基于拷貝構(gòu)造和拷貝賦值,而在一般情況下,這兩種函數(shù)都可能拋出異常。因此,當(dāng)你編寫一個自定義版本的 swap 時,在典型情況下你不僅要提供一條更高效的交換對象值的方式,同時你也要提供一個不拋出異常的版本。作為一條一般的守則,這兩條 swap 的特征是相輔相成的,因為高效的 swap 同時也基于內(nèi)建數(shù)據(jù)類型的操作(諸如 pimpl idiom 中使用的指針),同時內(nèi)建數(shù)據(jù)類型的操作決不會拋出異常。

銘記在心

在對你的類型使用 std::swap 時可能會造成效率低下時,可以提供一個 swap 成員函數(shù)。確保你的 swap 不要拋出異常。

如果你提供了一個 swap 的成員函數(shù),那么同時要提供一個非成員函數(shù) swap 來調(diào)用這一成員。對于類而言(而不是模板),還要提供一個 std::swap 的特化版本來調(diào)用 swap 成員函數(shù)。

在調(diào)用 swap 時,要為 std::swap 使用一條 using 聲明,然后在調(diào)用 swap 時,不要做出名字空間的限制。

對用戶自定義類型而言,提供 std 的完全特化版本不成問題,但是決不要嘗試在 std 中添加全新的內(nèi)容。

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲人成网站精品片在线观看| 欧美视频在线观看视频极品| 久久九九有精品国产23| 一区二区欧美在线观看| 亚洲看片一区| 一区二区三区国产在线| 亚洲特级片在线| 亚洲在线一区| 久久精品国产免费| 免费观看成人| 亚洲伦理在线| 亚洲影院色在线观看免费| 亚洲欧美激情四射在线日| 小黄鸭精品密入口导航| 久久精品国产一区二区三区免费看| 欧美一级在线亚洲天堂| 久久久午夜视频| 欧美黄色影院| 国产女优一区| 亚洲激情电影在线| 亚洲午夜av在线| 久久久国产成人精品| 欧美激情中文字幕一区二区| 一本一道久久综合狠狠老精东影业| 亚洲一级二级在线| 麻豆精品视频在线观看| 国产精品久久久久久久久久久久久| 国产婷婷色一区二区三区在线 | 久久精品一级爱片| 欧美剧在线观看| 国产一区二区三区网站| 99国产精品| 老司机精品视频网站| 亚洲图片在线观看| 欧美本精品男人aⅴ天堂| 国产精品午夜在线| 99re8这里有精品热视频免费 | 午夜一级在线看亚洲| 欧美v国产在线一区二区三区| 亚洲伦理在线免费看| 久久九九电影| 国产精品性做久久久久久| 亚洲日本成人| 噜噜噜躁狠狠躁狠狠精品视频| 一区二区三区视频免费在线观看| 猛男gaygay欧美视频| 国产视频丨精品|在线观看| 亚洲天堂av图片| 亚洲人成毛片在线播放女女| 久久久国产成人精品| 国产欧美精品一区二区色综合| 一区二区日韩| 亚洲精品欧美极品| 裸体女人亚洲精品一区| 狠狠综合久久| 久久影视精品| 久久国产夜色精品鲁鲁99| 国产日产精品一区二区三区四区的观看方式 | 亚洲激情在线| 欧美高清视频在线播放| 久久香蕉国产线看观看av| 国产一区深夜福利| 久久免费精品日本久久中文字幕| 亚洲欧美中文字幕| 国产欧美精品日韩精品| 午夜一区二区三区不卡视频| 一本一本久久a久久精品综合妖精| 欧美日韩高清在线播放| 亚洲午夜未删减在线观看| 日韩午夜在线电影| 欧美三级资源在线| 午夜精品久久久久久久久久久久| 国产精品99久久久久久久女警 | 亚洲黄一区二区| 亚洲国产精品第一区二区| 免费成人性网站| 99国内精品| 中文国产亚洲喷潮| 国产毛片一区| 欧美顶级艳妇交换群宴| 欧美激情精品久久久久| 亚洲午夜精品国产| 亚洲欧美日韩电影| 在线观看欧美成人| 亚洲精品久久久久| 国产精品一区2区| 蜜桃久久精品乱码一区二区| 欧美国产一区二区三区激情无套| 亚洲一区自拍| 久久久久久一区| 中文亚洲欧美| 久久九九免费视频| 99成人精品| 国产手机视频一区二区| 欧美一区国产一区| 久久亚洲精品一区二区| 日韩午夜激情电影| 亚洲一区一卡| 亚洲欧洲视频在线| 国产精品99久久久久久有的能看| 国内偷自视频区视频综合| 亚洲人午夜精品| 好吊妞**欧美| 亚洲视频视频在线| 亚洲破处大片| 性欧美大战久久久久久久免费观看| 亚洲欧洲另类国产综合| 亚洲宅男天堂在线观看无病毒| 在线观看不卡| 午夜精品久久久久99热蜜桃导演| 亚洲精品资源美女情侣酒店| 亚洲欧美日韩国产精品| 野花国产精品入口| 久久在线精品| 玖玖综合伊人| 国产日韩欧美高清| 99热精品在线观看| 亚洲美洲欧洲综合国产一区| 欧美在线观看你懂的| 亚洲天堂免费在线观看视频| 久久欧美肥婆一二区| 欧美一区三区二区在线观看| 欧美日韩国产成人在线观看| 欧美成人在线免费观看| 国产欧美日韩综合一区在线观看 | 中日韩高清电影网| 99re66热这里只有精品3直播| 久久久91精品国产| 欧美一区二视频在线免费观看| 欧美日韩国产另类不卡| 亚洲福利视频二区| 91久久视频| 欧美高清在线视频| 欧美成人午夜剧场免费观看| 韩日欧美一区| 欧美伊人久久大香线蕉综合69| 亚洲欧美日韩国产中文在线| 欧美揉bbbbb揉bbbbb| 亚洲人成毛片在线播放| 日韩视频专区| 欧美日韩精品一区二区三区| 91久久线看在观草草青青| 最新国产の精品合集bt伙计| 久久手机免费观看| 欧美韩国日本一区| 亚洲人成网站在线播| 欧美大秀在线观看| 亚洲肉体裸体xxxx137| 亚洲精品美女久久久久| 欧美精品啪啪| 一区二区三区日韩在线观看| 亚洲免费一在线| 国产精品资源在线观看| 久久www成人_看片免费不卡| 美国成人直播| 日韩视频在线免费观看| 欧美三日本三级三级在线播放| 国产精品视频网| 在线视频亚洲欧美| 欧美一区日本一区韩国一区| 黄色亚洲大片免费在线观看| 久久久中精品2020中文| 欧美a级在线| 夜夜夜久久久| 国产午夜精品久久| 欧美成年人视频网站| 夜夜嗨av一区二区三区网页| 午夜精品一区二区三区电影天堂 | 亚洲乱码视频| 久久se精品一区二区| 亚洲国产成人精品久久| 欧美日韩免费观看一区二区三区| 亚洲先锋成人| 欧美激情1区| 欧美一区二区三区男人的天堂 | 美女主播一区| 亚洲天堂久久| 欧美大秀在线观看| 亚洲欧美在线磁力| 亚洲黄色成人| 国产女人精品视频| 欧美国产免费| 久久精品卡一| 亚洲一区在线观看视频 | 国产精品久久久久久久久久妞妞 | 久久精品一区二区三区中文字幕| 欧美国产精品| 久久本道综合色狠狠五月| 亚洲精品一区中文| 国产有码在线一区二区视频| 欧美精品在线观看播放| 久久精品在这里| 亚洲一区国产精品| 亚洲精品免费在线观看| 久久手机免费观看| 欧美一区二区三区四区在线观看地址| 亚洲人成绝费网站色www| 国产中文一区| 国产日韩欧美综合在线| 国产精品久久久久久超碰|