條款25 考慮寫出乙個不丟擲異常的swap函式

2021-08-14 19:58:08 字數 2723 閱讀 5332

首先本篇部落格的主要思想是:系統自帶的swap函式有時候不能滿足我們的需求,所以在一些情況下我們就需要自己去寫swap函式。此條款的主要內容就是告訴你該如何去寫你要的swap函式,下面開始正文來好好地介紹一下此條款的內容

1.首先來看一下庫裡面給的swap函式的原型

可以看出來標準程式庫提供的swap函式是這樣的實現的,通過拷貝和賦值來實現。可以看出來他也沒有提供異常安全,所以在我們自己寫的swap函式中一定要注意異常安全這個問題。因為庫中給的swap函式對於簡單內建型別的置換還是可以的,但是對於一些稍微複雜一點的了,類型別的話就會顯得效率比較低下。比如下面這種情況

class widgetimpl;//類

class widget

;

如果我們要交換兩個widget物件的話,庫中的swap就會顯得效率很低,因為要複製三個widget物件,如果widgetimpl類中的成員也是自定義型別的話,呢麼就會複製更多的東西,造成效率低下。上面的這個例子我們可以很明顯得看出來。我們只需要交換內部的指標就可以了,沒必要複製那麼多次無用功的物件。所以我們可以想到

特化庫中的swap函式,**如下:

namespace std 

}

這樣我們企圖如果引數是widget的時候可以直接條用全特化的函式,從而實現將指標傳過去進行交換。可是這個函式可能無法通過編譯,因為widget中的指標成員pimpl是私有的。所以我們應該想辦法解決這個問題,那就是提供乙個成員函式或者友元函式使其可以訪問指標成員不就可以了嘛,在這裡我們採取成員函式(這裡使用特化版本來呼叫swap函式)。**如下:

class widget 

};namespace std

}

在這裡就可以通過特化版的函式來呼叫成員函式,從而使成員函式訪問成員將其私有成員函式作為實參呼叫swap函式從而達到我們想要的效果。

這樣寫不但能通過編譯而且和stl容器有一致性,因為所有的stl容器也都有public的swap函式和特化庫中的swap函式(呼叫public swap函式)。

上面闡述的問題都是類型別的問題,可以按照我們所說得解決問題,但是如果是類模板型別的話呢?

templateclass widget {};

templateclass widgetimpl{};

現在上面的兩個類都是模板型別的了。

我們可能會想到這麼改造一下函式的特化就可以了

namespace std 

}

可是不行呀,因為c++規定函式只能是全特化,而上面的這種形式就是偏特化了,所以行不通。。。。所以如果你打算偏特化乙個函式的時候,可以給這個函式新增過載函式,

比如這個樣子就是std::swap函式的過載了

namespace std 

}

可是事與願違,儘管這樣是個好方法,但是c++規定又不允許了,因為std是乙個特殊的命名空間,客戶可以特化裡面的模板,但是不可以新增新的模板類或者模板函式到std裡面去,std的內容完全由c++標準委員會決定,委員會禁止我們新增已經宣告好了的東西,雖然這種違禁行為可能會被編譯和執行,但是這是不明確的行為,所以不要給std新增新東西,所以此路不同。。。不同那我們也得想辦法解決呀,

解決方法就是宣告乙個非成員swap函式,但不將其宣告為std中swap的特化版本了,為了方便和更好的說明一些問題我們將其都定義在乙個新的命名空間中

namespace widgetstuff 

}; templatevoid swap(widget& a, widget& b)

}

現在這樣的話任何地點任何**如果打算交換wifget物件的話,都會呼叫widgetstuff的swap函式。

這種寫法對於類型別和類模板型別都行的通,但是這並不是最完美的,因為如果你想讓你的類專屬的swap函式在更可能多的語境下唄呼叫的話,我們應該同時在這個類所在的命名空間中寫乙個非成員函式和std的特化版本。

這樣的話如果

一旦編譯器看到對swap的呼叫,他們便查詢適當的swap函式並呼叫,c++的名稱查詢法則確保將找到全域性作用域或t所在之命名空間內的任何t專屬的swap。如果t是widget並位於命名空間widgetstuff內,編譯器會使用「實參查詢規則」找出widgetstuff命名空間中的swap,如果沒有t專屬的swap存在,編譯器就是用庫中的swap函式,這就取決於using std::swap這句話的作用。而然編譯器還是比較喜歡特化版本的swap如,而非一般化的template。

致此大致的swap函式就說完了。

這裡有三個要求每個都好好理解

如果你覺得庫的swap的效率不夠(意味著你使用了指標成員的這種用法)

1.提供乙個public swap成員函式,讓他來呼叫swap來置換你的指標的值

2.在你的類或者模板所在的命名空間內提供乙個非成員函式swap,讓他來呼叫成員函式

3.如果你正在編寫乙個類而不是模板類的話,記得特化swap,並讓他來呼叫你的swap成員函式

第乙個準確的說是2 3 的基礎,因為2 3 所呼叫的swap成員函式就是1中的

最後一點要強調的就是不要在你寫的成員swap函式中丟擲異常,因為你的這個函式是最終的最簡單的版本來呼叫庫函式的,所以效率最高這也往往意味著呼叫的庫函式的實參都是內建型別的,而內建型別的操作絕對不會丟擲異常的!

條款25 考慮寫出乙個不丟擲異常的swap函式

swap原本只是stl的一部分,後面成為異常安全程式設計的脊柱,及處理自我賦值安全性的乙個常見機制。例子 標準程式庫提供的swap演算法 namespace s td 要求 型別t支援copying 通過copying建構函式和copyassignment操作符完成 上述 主要涉及三個物件的複製,但...

條款25 考慮寫出乙個不拋異常的swap函式

總結 如果 std swap 對於你的型別來說是低效的,請提供乙個 swap 成員函式,並確保你的 swap 不會丟擲異常。如果你提供乙個成員 swap,請同時提供乙個呼叫成員swap的非成員swap。對於類 非模板 還要特化 std swap。呼叫swap時,請為std swap使用乙個using...

考慮寫出乙個不丟擲異常的swap函式

1 當std swap對你的型別效率不高時,提供乙個swap成員函式,並確定這個函式不丟擲異常。2 如果你提供乙個member swap函式,也該提供乙個non member swap函式,後者用來呼叫前者。對於class 而非template 也請特化std swap 3 呼叫swap時應針對st...