日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

《Effective C++》item25:考虑写出一个不抛异常的swap函数

發(fā)布時間:2025/3/14 c/c++ 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《Effective C++》item25:考虑写出一个不抛异常的swap函数 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?std::swap()是個很有用的函數(shù),它可以用來交換兩個變量的值,包括用戶自定義的類型,只要類型支持copying操作,尤其是在STL中使用的很多,例如:

int main(int argc, _TCHAR* argv[]) { int a[10] = {1,2,3,4,5,6,7,8,9,10}; vector<int> vec1(a, a + 4); vector<int> vec2(a + 5, a + 10); swap(vec1, vec2); for (int i = 0; i < vec1.size(); i++) { cout<<vec1[i]<<" "; } return 0; }

?

? ? ? ? 上面這個例子實現(xiàn)的是兩個vector的內容的交換,有了swap函數(shù),省去了很多的麻煩!What a fucking convenient!

?

?

一、swap的原理

?

? ? ? ? 缺省的swap的原理其實很簡單,就是將兩對象的值彼此賦予對方,其實現(xiàn)過程大致如下:

?

namespace std{ template<typename T> void swap(T&a, T& b){ T temp(a); a=b; b=temp; } } ?

? ? ? ??swap的實現(xiàn)是通過被交換類型的copy構造函數(shù)和賦值操作符重載實現(xiàn)的,會涉及到三個對象的復制。所以說,要對自定義的類型調用swap實現(xiàn)交換,必須首先保證自定義類型的copy構造函數(shù)和賦值操作符重載函數(shù)。

?

?

二、swap的缺陷

?

? ? ? ? 缺省的swap最主要的問題就是:當對象內部包含指針成員時,它不僅要復制3三次被交換的對象,還要復制3次對象成員,而且復制的是指針對象所指向的內容!例如:

// SwapTest.cpp : 定義控制臺應用程序的入口點。 #include "stdafx.h" #include <iostream> #include <vector> using namespace std; class Point{ private: int x,y; public: Point():x(0),y(0){}; Point(int a, int b):x(a),y(b){}; void Print(){ cout<< x << " "<< y <<endl; } int GetX(){ return x; } int GetY(){ return y; } }; class Line{ private: Point *px, *py; public: Line():px(),py(){}; Line(int a,int b,int c,int d):px(new Point(a,b)), py(new Point(c, d)){}; Line(const Line& li){ px = new Point(*li.px); py = new Point(*li.py); } Line& operator=(const Line& li){ Point *p = px; px = new Point(*li.px); delete p; p = py; py = new Point(*li.py); delete p; return *this; } void Print(){ cout<<"( "<<px->GetX()<<" "<<px->GetY()<<" "<<py->GetX()<<" "<<py->GetY()<<" )"<<endl; } }; int main(int argc, _TCHAR* argv[]) { Line l1(1,1,2,2), l2(3,3,4,4); swap(l1, l2); l1.Print(); return 0; }

?

? ? ? ? 一旦要置換兩個Line對象值,swap需要復制三個Line,還要復制六個Point對象,詳細可以看賦值運算符重載函數(shù),這樣是非常低效的,尤其是當Line的數(shù)據(jù)成員非常龐大的時候,實際上我們只需要交換各自成員的指針就可以了!

三、swap的改進方案

? ? ? ? 我們希望告訴std::swap:當Line被置換時,真正該做的是置換騎內部的px和py指針。實現(xiàn)這個過程有幾個方案,我們先看最簡單的方案:

方案一:將std::swap針對Line特化

?

? ? ? ? C++規(guī)定:通常不允許改變std命名空間內的任何東西,但是可以為標準template(如swap)制造特化版本,使他專屬于我們自己的class(例如Line)。

? ? ? ? 根據(jù)這個性質,我們可以對std:swap針對Line進行特化。我們可以這樣特化swap:

namespace std{ template<> void swap<Line>(Line& l1, Line& l2){ // std::swap()的特化版本,std::swap()只可以特化,不可以重載 cout<<"swap of std is called......"<<endl; l1.swap(l2); } }

? ? ? ? 在這個代碼中,“template<>”表示它是std::swap的一個全特化版本,函數(shù)名之后的“<Line>"表示這一特化版本針對”T是Line”而設計。

?

? ? ? ? 完整的方案如下:

?

// SwapTest.cpp : 定義控制臺應用程序的入口點。 #include "stdafx.h" #include <iostream> #include <vector> using namespace std; class Point{ private: int x,y; public: Point():x(0),y(0){}; Point(int a, int b):x(a),y(b){}; void Print(){ cout<< x << " "<< y <<endl; } int GetX(){ return x; } int GetY(){ return y; } }; class Line{ private: Point *px, *py; public: Line():px(),py(){}; Line(int a,int b,int c,int d):px(new Point(a,b)), py(new Point(c, d)){}; void swap(Line& l){ //Line成員函數(shù),用以實現(xiàn)指針成員交換 cout<<"swap of Line is called......"<<endl; using std::swap; swap(px, l.px); // 交換指針 swap(py, l.py); } void Print(){ cout<<"( "<<px->GetX()<<" "<<px->GetY()<<" "<<py->GetX()<<" "<<py->GetY()<<" )"<<endl; } }; namespace std{ template<> void swap<Line>(Line& l1, Line& l2){ // std::swap()的特化版本,std::swap()只可以特化,不可以重載 cout<<"swap of std is called......"<<endl; l1.swap(l2); } } int main(int argc, _TCHAR* argv[]) { Line l1(1,1,2,2), l2(3,3,4,4); swap(l1, l2); l1.Print(); return 0; }

? ? ? ? 在這個例子中,一共出現(xiàn)了5次swap這個函數(shù):

?

? ? ? ? 第一次是main中調用的swap,這個調用的是我們自定義的std::swap()的特化版本

? ? ? ? 第二次是我們自己定義的std::swap()對Line類型的特化,在函數(shù)名前面有“template<>”

? ? ? ? 第三次是對Line特化的swap中調用的swap,也就是l1.swap(l2),這個很明顯是調用Line類型的swap()成員函數(shù)

? ? ? ? 第四次是Line類型中的成員函數(shù),void swap(Line& l),這個public函數(shù)的目的是供給非Line成員函數(shù)調用的,也就是特化版本的swap,因為只有類的成員函數(shù)才可以調用類的private成員變量

? ? ? ? 第五次是Line成員函數(shù)swap調用的swap,這個swap調用前面有個using std::swap的聲明,表示后面調用的是std中的原始swap,當然不是特化版本的swap
? ? ? ? 其中可以被調用的swap有3個,std中原始的swap、std::swap的特化版本、Line中的成員函數(shù)swap,這3個函數(shù)中,真正給用戶調用的只有第一個swap,也就是std::swap的Line特化版。通過這一系列函數(shù)就可以實現(xiàn)Line對象中指針成員的指針的交換,而不是Line對象整個的交換。

? ? ? ? 這種方式和STL容器有一致性,因為所有的STL容器也都提供有public swap成員函數(shù)和std::swap特化版本(用以調用前者)

? ? ? ??

方案二、重載特化的std::swap

?

? ? ? ? 上面這種方式是針對Line和Point都是非template class,現(xiàn)在假設Line和Point都是template class,那么這種方式還可不可以了?

? ? ? ? 假設Line類和Point類都是template class,如下定義:

#include <iostream> #include <string.h> template<typename T> class Point{ private: T x,y; public: Point():x(0),y(0){}; Point(T a, T b):x(a),y(b){}; void Print(){ std::cout<< x << " "<< y <<endl; } T GetX(){ return x; } T GetY(){ return y; } }; template<typename T1, typename T2> class Line{ private: T1 px,py; public: Line():px(),py(){}; Line(T2 a,T2 b,T2 c,T2 d):px(T1(a, b)), py(T1(c, d)){}; void Print(){ std::cout<<"( "<<px.GetX()<<" "<<px.GetY()<<" "<<py.GetX()<<" "<<py.GetY()<<" )"<<std::endl; } void swap(Line<T1, T2>& l){ std::cout<<"swap of Line is called......"<<std::endl; using std::swap; swap(px, l.px); swap(py, l.py); } }; namespace std { template<typename t1="" typename="" t2=""> void swap(Line<t1 t2="">& l1, Line<t1 t2="">& l2){ cout<<"swap of std is called......"<<std::endl; l1.swap(l2); } } int main() { Line<Point<double>, double> l1(1, 1, 2, 2), l2(3, 3, 4, 4); std::swap(l1, l2); l1.Print(); return 0; }

?

? ? ? ? 其中std里面的swap函數(shù)就是對std::swap的一個重載版本,然而,這個方式并不是特別的推薦,按照《effective c++》中的說法,這是一種非法的方式,是被C++標準禁止的,雖然能夠編譯和運行通過。

方案三、非特化非重載的non-member swap

? ? ? ? 我們可以聲明一個非Line類成員函數(shù)swap,讓其調用Line的成員函數(shù)swap,這個非成員swap也非特化的std::swap,如下所示:

  • #include "stdafx.h" #include <iostream> #include <string.h> template<typename T> class Point{ private: T x,y; public: Point():x(0),y(0){}; Point(T a, T b):x(a),y(b){}; void Print(){ std::cout<< x << " "<< y <<endl; } T GetX(){ return x; } T GetY(){ return y; } }; template<typename T1, typename T2> class Line{ private: T1 px,py; public: Line():px(),py(){}; Line(T2 a,T2 b,T2 c,T2 d):px(T1(a, b)), py(T1(c, d)){}; void Print(){ std::cout<<"( "<<px.GetX()<<" "<<px.GetY()<<" "<<py.GetX()<<" "<<py.GetY()<<" )"<<std::endl; } void swap(Line<T1, T2>& l){ std::cout<<"swap of Line is called......"<<std::endl; using std::swap; swap(px, l.px); swap(py, l.py); } }; template<typename T1, typename T2> void swap(Line<T1, T2>& a, Line<T1, T2>& b){ std::cout<<"swap of non-member is called......"<<std::endl; a.swap(b); } int main() { Line<Point<double>, double> l1(1, 1, 2, 2), l2(3, 3, 4, 4); std::swap(l1, l2); l1.Print(); return 0; }

    ?

  • ? ? ? ? 其實就是將方案二的std::swap重載改成了自定義的非成員函數(shù),原理依然一樣!

    remember:

    ? ? ? ? 1.當std::swap對你的類型效率不高時,提供一個swap成員函數(shù),并確定這個函數(shù)不拋出異常。
    ? ? ? ? 2.如果你提供一個member swap,也該提供一個non-member swap用來調用前者。對于class(而非template),也請?zhí)鼗痵td::swap。
    ? ? ? ? 3.調用swap時應針對std::swap使用using聲明式,然后調用swap并且不帶任何“命名空間資格修飾符”。
    ? ? ? ? 4.為“用戶定義類型”進行std template全特化是好的,但千萬不要嘗試在std內加入某些對std而言全新的東西。

    轉載于:https://www.cnblogs.com/wuchanming/p/3735189.html

    總結

    以上是生活随笔為你收集整理的《Effective C++》item25:考虑写出一个不抛异常的swap函数的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。