Effective C++学习第七天
條款23:寧以non-memeber、non-friend替換member函數(shù)
? ? ? ? ? non-member/non-friend可以給對象帶來更大的封裝性,從兩個方面來考慮:1)考慮封裝,越多東西被封裝,它們就越不可見,就越少人看到它,就會越有彈性去改變它;2)考慮對象內(nèi)的數(shù)據(jù),越少代碼可以看到數(shù)據(jù),越多的數(shù)據(jù)被封裝,那么我們就越能自由地改變對象數(shù)據(jù);
? ? ? ? ? 在C++中,可以讓所有的non-member函數(shù)放在同一個命名空間內(nèi),然后使用那個命名空間;C++標(biāo)準(zhǔn)程序庫并不是單一、整體、龐大的頭文件,而是很多頭文件組合而成的,這就允許客戶只對他們所有的那部分系統(tǒng)形成編譯相依;
條款24:若所有參數(shù)都需類型轉(zhuǎn)換,用non-member代替member函數(shù)
? ? ? ? ? ? ?對于參數(shù)都允許發(fā)生隱式轉(zhuǎn)換的函數(shù),使用non-member函數(shù)可以使你的類功能更加具有一致性,而且還支持混合式算術(shù)編程;具體代碼分析如下:
? ? ? ? ?const rational operator*(const rational&rhs)const{
? ? ? ? ? ? ? ?return rational(this->numerator( )*rhs.numerator( ),this->denominator( )*rhs.denominator( );
}
rational onehalf(1,2);
rational result=2*onehalf;//錯誤,調(diào)用實際情況是2.operator(&2,onehalf);//不能對常量取地址操作
rational result=onehalf*2;//正確
修改為non-member函數(shù)
const rational operator*(const rational& lhs ,? const rationa & rhs){
? ? ? return rational(lhs.numerator( )*rhs.numerator( ),lhs.denominator( )*rhs.denominator( );
}
結(jié)論:如果你需要為某個函數(shù)的所有參數(shù)(this指針?biāo)傅哪莻€隱喻參數(shù))進(jìn)行類型轉(zhuǎn)換,那么這個函數(shù)必須是個non-member;
條款25:考慮寫出一個不拋異常的swap函數(shù)
? ? ? ? ? ? ?swap原本是STL中的一部分,后成為異常安全性編程的脊柱以及用來處理自我賦值可能的一種常見機(jī)制;
?1.缺省情況下,std標(biāo)準(zhǔn)庫的swap算法:
? ? ? ? ? ? ? ? ? template<typename T>
? ? ? ? ? ? ? ? ? void swap(T&a,T&b){T temp(a);a=b;b=temp;}//實現(xiàn)是基于T的copy構(gòu)造函數(shù)和copy assignment完成
2.通常情況下,數(shù)據(jù)的表現(xiàn)形式都是“以指針指向一個對象,內(nèi)含真正的數(shù)據(jù)”,這種設(shè)計模式通常變現(xiàn)為pimpl(pointer to implementation)手法,如:
class widgetimpl{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? class widget{
private:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?private:
? ? ? ? ? int a ,b,c;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?widgetimpl*pimpl;
? ? ? ? ? std::vector<double>v;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? };
}
如果需要交換兩個widget對象,那么我們唯一需要做的就是交換兩個pimpl指針,但是普通的swap算法卻是復(fù)制了三次widgetimpl;
解決上述問題的一個方法:就是將std::swap針對widget全特化(模板函數(shù)的一個實例),然后用widget的成員函數(shù)調(diào)用它(寫錯了)用全特化的swap函數(shù)來調(diào)用public swap成員函數(shù),具體代碼如下:
class widget{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?namespace std{
public:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? template<>//針對widget的特化版本
? ? ? ? ? void swap(widget&other){? ? ? ? ? ? ? ? ? ? ? ? ? ?void swap<widget>(widget&a.widget&b){
? ? ? ? ? using std::swap;//讓std內(nèi)的swap可用? ? ?? ? ? ? ? ? ? ? ? ? ? ?a.swap(b);? ? ? ? ? ? //調(diào)用swap成員函數(shù);
? ? ? ? ?swap(pimpl,other.pimpl);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
};? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}
3.如果widget和widgetpimpl都是class templates而非class,那么我們重新定義非成員函數(shù);
? ? ? ?template<typename T>
? ? ? void swap<widget<T>>(widget<T>&a,widget<T>&b){ a.swap(b);}? ? ? ?//不合法,錯誤
原因是我們企圖對function template偏特化,但是C++只允許對class template偏特化;
引申:std是一個特殊的命名空間,其管理規(guī)則也比較特殊,客戶可以全特化(實例化)std內(nèi)的template,但是不可以添加新的templates(class或者templates或者其他的任何東西)到std里頭,C++禁止這類行為,但是編譯器卻不會報錯,但是軟件可能會出現(xiàn)不可預(yù)期的行為;
? ? 解決上述問題的一個辦法:添加一個重載函數(shù)來代替我們要做的偏特化一個function template行為,我們還是聲明一個non-member swap函數(shù)讓他調(diào)用member swap,但不再聲明將那個non-member swap聲明為std::swap的特化版本,具體實現(xiàn)代碼如下:
?template<typename T>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???template<typename T>
class widget{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?void swap(T&obj1,T&obj2){
public:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?using std::swap;
? ? ? ? void swap(widget&other){? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?swap(obj1,obj2);//T型對象的最佳調(diào)用swap版本
? ? ? ??? using std::swap;? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}? ?//優(yōu)先調(diào)用T專屬版本,即public swap成員函數(shù)? ? ? ? ? ? ? ? ? ??
? ? ? ? ?swap(pimpl,other.pimpl);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//并在該版本不存在的情況下調(diào)用std內(nèi)一般化的版本
}
結(jié)論:1)如果swap缺省實現(xiàn)碼可以實現(xiàn)你的class或者class template提供可接受的效率,你不需要做任何事情;
? ? ? ? ? 2)如果swap缺省碼不夠?qū)崿F(xiàn)你的效率,你可以:
? ? ? ? ? ? ? ?a)提供一個public swap成員函數(shù),讓它處理兩個對象值;
? ? ? ? ? ? ? ?b)在你所在的class或者template所在的命名空間內(nèi)提供一個non-member swap,并令它調(diào)用上述swap成員函數(shù);
? ? ? ? ? ? ? ?c)如果你在編寫一個class(而非class template),為你的class特化std::swap,并令它調(diào)用你的swap成員函數(shù)。
? ? ? ? 3)如果你調(diào)用swap,確定包含一個using聲明式,以便std::swap在你的函數(shù)內(nèi)曝光,然后不加namespace,單純調(diào)用swap(使swap函數(shù)有更多的選擇);
成員版的swap絕不可能拋出異常,因為swap的一個最好應(yīng)用就是幫助classes提供強(qiáng)烈的異常安全性保障(以copy構(gòu)造函數(shù)和copy assignment操作符為基礎(chǔ)的);
引申:關(guān)于模板的全特化和偏特化描述,可以參考博文:https://www.cnblogs.com/yyehl/p/7253254.html
總結(jié)
以上是生活随笔為你收集整理的Effective C++学习第七天的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在服务器中部署node项目 财富值
- 下一篇: 成都大熊猫繁育研究基地做地铁几号线