真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

effectivec++讀書筆記4-創(chuàng)新互聯(lián)

設(shè)計(jì)class猶如設(shè)計(jì)type

新type的對(duì)象應(yīng)該如何被創(chuàng)建和銷毀?:這會(huì)影響到你的class的構(gòu)造函數(shù)和析構(gòu)函數(shù)以及內(nèi)存分配函數(shù)和釋放函數(shù)的設(shè)計(jì)。

目前成都創(chuàng)新互聯(lián)公司已為近1000家的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬空間、網(wǎng)站改版維護(hù)、企業(yè)網(wǎng)站設(shè)計(jì)、安陽縣網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。

對(duì)象的初始化和對(duì)象的賦值有什么樣的區(qū)別?這答案決定你的構(gòu)造函數(shù)和賦值操作符的行為,以及其間的差異。很重要的是別混淆了“初始化”和“賦值”,因?yàn)樗鼈儗?duì)應(yīng)不同的函數(shù)調(diào)用。

新type的對(duì)象如果被passed by value(以值傳遞),意味著什么?記住,copy構(gòu)造函數(shù)用來定義一個(gè)type的pass-by-value該如何實(shí)現(xiàn)。

什么是新type的“合法值”?對(duì)于class的成員變量而言,通常只有某些數(shù)值集是有效的。那些數(shù)值決定了你的class必須維護(hù)的約束條件,也就決定了你的成員函數(shù)必須進(jìn)行的錯(cuò)誤檢查工作,它也影響函數(shù)拋出的異常、以及函數(shù)異常明細(xì)列。

你的新type需要配合某個(gè)繼承體系嗎?如果你繼承自某些既有的classes,你就受到那些classes的設(shè)計(jì)的束縛,特別是受到“它們的函數(shù)是virtual或non-virtual”的影響。如果你允許其他classes繼承你的class,那會(huì)影響你所聲明的函數(shù)--尤其是析構(gòu)函數(shù),是否為virtual。

你的新type需要什么樣的轉(zhuǎn)換?如何你允許類型T1之物被隱式轉(zhuǎn)換為類型T2之物,就必須在class T1內(nèi)寫一個(gè)類型裝換函數(shù)或者在class T2內(nèi)寫一個(gè)non-expliccit-noe-argument(可被單一實(shí)參調(diào)用)的構(gòu)造函數(shù)。如果你允許explicit構(gòu)造函數(shù)存在,就得寫出專門負(fù)責(zé)執(zhí)行轉(zhuǎn)換的函數(shù),且不得為類型轉(zhuǎn)換操作符或non-explicit-one-argument構(gòu)造函數(shù)。

什么樣的操作符合函數(shù)對(duì)此新type而言是合理的?這個(gè)取決于你將為你的class聲明哪些函數(shù)。

什么樣的標(biāo)準(zhǔn)函數(shù)應(yīng)該駁回?那些正是你必須聲明為private者。

誰該采用新type的成員?這個(gè)跟設(shè)計(jì)成員函數(shù)有關(guān)。你可以決定哪個(gè)成員為private,哪個(gè)為protected,哪個(gè)為private。

你的新type有多么一般化?或許你其實(shí)并非定義一個(gè)新type,而是定義一整個(gè)types家族。果真如此你就不應(yīng)該定義一個(gè)新class,而是定義一個(gè)新的class template。

你真的需要一個(gè)新的type嗎?如果只是定義新的派生類以便為既有的class添加機(jī)能,那么說不定單純定義一或多個(gè)非成員函數(shù)或模板,更能夠達(dá)到目標(biāo)

寧以pass-by-reference-to-const替換pass-by-value

傳值調(diào)用會(huì)造成代碼執(zhí)行效率低

例如:

class Person
{
public:
    Person();
    virtual ~Person();
private:
    std::string name;
    std::string address;
};
 
class Student :public Person
{
public:
    Student();
    ~Student();
private:
    std::string schoolName;
    std::string schoolAddress;
};
 
bool validateStudent(Student s);


int main()
{
    Student plato;          //定義Student對(duì)象
    validateStudent(plato); //傳值調(diào)用
    return 0;
}

可以看下傳值過程中做了那些事情?

①執(zhí)行6次構(gòu)造函數(shù):plato傳入進(jìn)函數(shù)的時(shí)候,需要調(diào)用1次Student的構(gòu)造函數(shù),Student構(gòu)造函數(shù)執(zhí)行前需要先構(gòu)造Person ,因此還會(huì)調(diào)用1次Person類的構(gòu)造函數(shù)。Person和Student兩個(gè)類中共有4個(gè)string成員變量,因此還需要調(diào)用string的構(gòu)造函數(shù)4次
②執(zhí)行6次析構(gòu)函數(shù):當(dāng)函數(shù)執(zhí)行完成之后,傳入validateStudent函數(shù)中的plato副本還需要執(zhí)行析構(gòu)函數(shù),那么執(zhí)行析構(gòu)函數(shù)的時(shí)候分別要對(duì)應(yīng)執(zhí)行6次析構(gòu)函數(shù)(Student+Person+4個(gè)string成員變量)

改進(jìn):

這種傳遞方式的效率高得多:沒有任何構(gòu)造函數(shù)或析構(gòu)函數(shù)被調(diào)用,因?yàn)闆]有任何新對(duì)象被創(chuàng)建

以by reference方式傳遞參數(shù)也可以避免slicing(對(duì)象切割)問題

例如:

class Window
{
public:
    std::string name()const;
    virtual void display()const;
};
 
class WindowsWithScrollBars :public Window
{
public:
    virtual void display()const;
};
 
void printNameAndDisplay(Window w)
{
    std::cout<< w.name();
    w.display();
}

當(dāng)執(zhí)行下面代碼時(shí):

int main()
{
    WindowsWithScrollBars wwsb;
    printNameAndDisplay(wwsb);  //WindowsWithScrollBars對(duì)象會(huì)被截?cái)?    return 0;
}
因?yàn)閜rintNameAndDisplay函數(shù)的參數(shù)為Window類型,所以即使我們傳入的是WindowsWithScrollBars類型的對(duì)象,那么這個(gè)對(duì)象會(huì)被截?cái)啵蝗indowsWithScrollBars對(duì)象中屬于基類(Window)的內(nèi)容,然后傳遞給函數(shù)
因此被截?cái)嘀?,不論如何調(diào)用,printNameAndDisplay函數(shù)中調(diào)用的display函數(shù)總是Window的display虛函數(shù),不會(huì)是WindowsWithScrollBars中的display虛函數(shù)。因?yàn)槎鄳B(tài)只會(huì)發(fā)生在基類指針/引用指向于派生類的情況下,此處沒有指針/引用

改進(jìn):

對(duì)于內(nèi)置類型建議傳值調(diào)用

C++編譯器的底層,是把引用以指針的形式實(shí)現(xiàn),因此引用傳遞就是指針的傳遞

如果你使用的對(duì)象屬于內(nèi)置類型(例如int),內(nèi)置類型都相當(dāng)小,傳值調(diào)用往往比引用傳遞的效率高些

這條規(guī)則在STL的迭代器和函數(shù)對(duì)象中都被廣泛引用

請(qǐng)記住:

1.盡量以pass-by-reference-to-const替換 pass-by-value。前者通常比較高效,并可避免切割問題(slicing problem)。
2. 以上規(guī)則并不適用于內(nèi)置類型,以及STL的迭代器和函數(shù)對(duì)象。對(duì)它們而言,pass-by-value往往比較適當(dāng)。
必須返回對(duì)象時(shí),別妄想返回其reference

自己要記住,任何時(shí)候看到一個(gè)reference聲明式,你都應(yīng)該立刻問自己,它的另一個(gè)名稱是什么?

看下面一段代碼:我們有一個(gè)用來變現(xiàn)有理數(shù)的class,內(nèi)含一個(gè)函數(shù)用來計(jì)算兩個(gè)有理數(shù)的乘積

下面的那一段代碼是合理的:

但如果返回值是引用,會(huì)存在什么問題呢?看下面的一些調(diào)用:

最后的那段代碼存在問題,期望“原本就存在一個(gè)其值為3/10的Rational對(duì)象”并不合理。如果operator*要返回一個(gè)reference指向如此數(shù)值,它必須自己創(chuàng)建那個(gè)Rational對(duì)象。

方案1:

stack 空間或在 heap空間創(chuàng)建對(duì)象,例如:

friend const Rational&
    operator*(const Rational& lhs, const Rational& rhs)
{
    Rational result(lhs.n*rhs.n, lhs.d*rhs.d);
    return result; //雖然編譯器不報(bào)錯(cuò),但是邏輯上是錯(cuò)誤的
}

原因:由于result是局部對(duì)象,函數(shù)結(jié)束之后局部對(duì)象(??臻g)就釋放了,引起返回其引用是無效的,也就是說該函數(shù)返回了一個(gè)已經(jīng)無效的對(duì)象的引用。

friend const Rational&
    operator*(const Rational& lhs, const Rational& rhs)
{
    Rational *result = new Rational(lhs.n*rhs.n, lhs.d*rhs.d);
    return *result;//雖然編譯器不報(bào)錯(cuò),但是邏輯上是錯(cuò)誤的
}

Rational w, x, y, z;
w = x*y*z; //相當(dāng)于operator*(operator*(x,y),z);

原因:在函數(shù)內(nèi),我們?cè)诙焉仙暾?qǐng)了一個(gè)對(duì)象,然后返回對(duì)象的引用,雖然函數(shù)結(jié)束之后對(duì)象仍存在,我們使用起來不會(huì)出錯(cuò),但是這個(gè)函數(shù)內(nèi)部建立的對(duì)象我們無法釋放(因?yàn)槲覀儫o法獲取其指針,不能進(jìn)行delete操作)。因此會(huì)造成內(nèi)存泄漏

方案2:返回一個(gè)靜態(tài)局部變量

例如:

friend const Rational&
    operator*(const Rational& lhs, const Rational& rhs)
{
    static Rational result; //靜態(tài)局部變量
 
    //result=...; 將lhs乘以rhs,然后將結(jié)果保存在result中
 
    return *result; //雖然編譯器不報(bào)錯(cuò),但是邏輯上是錯(cuò)誤的
}

再看下面的代碼:先不管多線程安全的問題

bool operator==(const Rational& lhs,const Rational& rhs);
int main()
{
    Rational a,b,c,d;
    //...
    if((a*b)==(c*d)){ //此if永遠(yuǎn)為真,因?yàn)閛perator*返回的都是同一個(gè)對(duì)象的引用

    }
    else{
    }
}

表達(dá)式((a*b) =- (c*d))總是被核算為true,不論a, b, c和d的值是什么!

原因:在operator== 被調(diào)用前,已有兩個(gè)operator*調(diào)用式起作用,每一個(gè)都返回reference指向operator*內(nèi)部定義的static Rational對(duì)象。因此operator==被要求將“operator*內(nèi)的 static Rational對(duì)象值”拿來和“operator*內(nèi)的 staticRational對(duì)象值”比較,那么當(dāng)我們每回調(diào)用operator*函數(shù)的時(shí)候,每回返回的都是同一個(gè)靜態(tài)局部變量的引用。

請(qǐng)記住:

絕不要返回pointer或reference指向一個(gè)local stack對(duì)象,或返回reference指向一個(gè)heap-allocated對(duì)象,或返回pointer或reference指向一個(gè)local static對(duì)象而有可能同時(shí)需要多個(gè)這樣的對(duì)象。
寧以non-member,non-friend替換member函數(shù)

象有個(gè)class用來表示網(wǎng)頁瀏覽器。這樣的 class可能提供的眾多函數(shù)中,有一些用來清除下載元素高速緩存區(qū)(cache of downloaded elements)、清除訪問過的URLs的歷史記錄等,例如:

許多用戶會(huì)想一整個(gè)執(zhí)行所有這些動(dòng)作,因此webBrowser也提供這樣-一個(gè)函數(shù):

那么是成員函數(shù)好還是非成員函數(shù)呢?

解釋:

1.提供non-member函數(shù)可允許對(duì)webBrowser 相關(guān)機(jī)能有較大的包裹彈性( packaging flexibility),而那最終導(dǎo)致較低的編譯相依度,增加 webBrowser的可延伸性。如果某些東西被封裝,它就不再可見。愈多東西被封裝,愈少人可以看到它。而愈少人看到它,我們就有愈大的彈性去變化它。
2.第二件值得注意的事情是,只因在意封裝性而讓函數(shù)“成為class 的 non-member”,并不意味它“不可以是另一個(gè)class 的member”

namespace和 classes不同,前者可跨越多個(gè)源碼文件而后者不能。一個(gè)像webBrowser這樣的class可能擁有大量便利函數(shù),某些與書簽(bookmarks>有關(guān),某些與打印有關(guān),分離它們的最直接做法就是將書簽相關(guān)便利函數(shù)聲明于一個(gè)頭文件,將cookie相關(guān)便利函數(shù)聲明于另一個(gè)頭文件,以此類推。

這正是C++標(biāo)準(zhǔn)程序庫的組織方式。

以此種方式切割機(jī)能并不適用于class成員函數(shù),因?yàn)橐粋€(gè)class必須整體定義,不能被分割為片片段段。一小部分。

將所有便利函數(shù)放在多個(gè)頭文件內(nèi)但隸屬同一個(gè)命名空間,意味客戶可以輕松擴(kuò)展這一組便利函數(shù)。他們需要做的就是添加更多non-member non-friend函數(shù)到此命名空間內(nèi)。

請(qǐng)記?。?/p>

寧可拿non-member non-friend 函數(shù)替換member函數(shù)。這樣做可以增加封裝性、包裹彈性(packaging flexibility)和機(jī)能擴(kuò)充性
若所有參數(shù)皆需類型轉(zhuǎn)換,請(qǐng)為此采用non-member函數(shù)

先看一段關(guān)于類型轉(zhuǎn)換的代碼:

如果這樣去使用代碼:

int main()
{
    Rational oneEighth(1, 8);
    Rational oneHalf(1, 2);
 
    Rational result = oneHalf*oneEighth;//正確
    result = result*oneEighth;          //正確
 
    return 0;
}

由于可以進(jìn)行隱式的類型轉(zhuǎn)換,因此我們還可以進(jìn)行下面的有理數(shù)相乘

int main()
{
    Rational oneHalf(1, 2);
 
    //2隱式轉(zhuǎn)換為Rational類型
    //然后下面代碼等價(jià)于result=oneHalf.operator*(2) 
    Rational result = oneHalf * 2; 
 
    return 0;
}

在上面的代碼中,編譯器知道你正傳遞一個(gè)int對(duì)象,但是operator*()需要的是Rational,因此它調(diào)用Rational構(gòu)造函數(shù)并賦予你所提供的int,就可以構(gòu)造出一個(gè)臨時(shí)的Rational對(duì)象,但是如果這樣使用:下面是錯(cuò)誤的代碼

Rational oneHalf(1, 2);
 
   //代碼相當(dāng)于2.operator*(oneHalf)
   Rational result = 2 * oneHalf; //錯(cuò)誤

oneHalf是一個(gè)內(nèi)含operator*函數(shù)的class的對(duì)象,所以編譯器調(diào)用該函數(shù)。然而整數(shù)2 并沒有相應(yīng)的class,也就沒有operator*成員函數(shù)。編譯器也會(huì)嘗試尋找可被以下這般調(diào)用的non-member operator*(也就是在命名空間內(nèi)或在global作用域內(nèi)):

從上面的例子我們可以看到,如果operator*()為成員函數(shù),在某些情況下即使存在隱式轉(zhuǎn)換也不能成功執(zhí)行。只有當(dāng)參數(shù)被列于參數(shù)列(parameter list)內(nèi),這個(gè)參數(shù)才是隱式類型轉(zhuǎn)換的合格參與者。地位相當(dāng)于“被調(diào)用之成員函數(shù)所隸屬的那個(gè)對(duì)象”—--即 this對(duì)象—一的那個(gè)隱喻參數(shù),絕不是隱式轉(zhuǎn)換的合格參與者。

例如:

class Rational
{
public:
    Rational(int numerator = 0, int denominator = 1);
    int numerator()const;
    int denominator()const;
private:
    //...
};
 
const Rational operator*(const Rational& lhs,const Rational& rhs)
{
    return Rational(lhs.numerator()*rhs.numerator,
        lhs.denominator()*lhs.denominator());
}

那么下面的調(diào)用都是正確的:

int main()
{
    Rational oneEighth(1, 8);
    Rational oneHalf(1, 2);
 
    Rational result;
 
    result = oneHalf*oneEighth;
    result = result*oneEighth;    
 
    result = oneHalf * 2;
    result = 2 * oneHalf;
    result = 2 * oneHalf*2;
    result = oneHalf * 2 * oneHalf;
 
    return 0;
}

請(qǐng)記?。?/p>

1.如果你需要為某個(gè)函數(shù)的所有參數(shù)(包括被this這指針很所指的那個(gè)隱喻參數(shù))進(jìn)行類型轉(zhuǎn)換,那么這個(gè)函數(shù)必須是個(gè)non-member
2.如果一個(gè)函數(shù)不建議聲明為member版本,那么就將該函數(shù)聲明為non-member版本,而不是friend函數(shù)

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購,新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧


網(wǎng)頁題目:effectivec++讀書筆記4-創(chuàng)新互聯(lián)
當(dāng)前鏈接:http://weahome.cn/article/csdpdc.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部