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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

c++primer读书笔记

發(fā)布時間:2023/12/14 c/c++ 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c++primer读书笔记 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
c++全局變量無聲明。多次定義會沖突。利用命名空間


c++四種cast操作符的區(qū)別
1.static_cast,支持子類指針到父類指針的轉(zhuǎn)換,并根據(jù)實際情況調(diào)整指針的值,反過來也支持,但會給出編譯警告,它作用最類似C風(fēng)格的“強(qiáng)制轉(zhuǎn)換”,一般來說可以認(rèn)為它是安全的。
2.dynamic_cast,支持父類指針到子類指針的轉(zhuǎn)換,并根據(jù)實際情況調(diào)整指針的值,和static_cast不同,反過來它就不支持了,會導(dǎo)致編譯錯誤,這種轉(zhuǎn)換是最安全的轉(zhuǎn)換;
3.reinterpret_cast,支持任何轉(zhuǎn)換,但僅僅是如它的名字所描述的那樣“重解釋”而已,不會對指針的值進(jìn)行任何調(diào)整,用它完全可以做到指鹿為馬,但很明顯,它是最不安全的轉(zhuǎn)換,使用它的時候,你得頭腦清醒,知道自己在干什么。
4.const_cast,這個轉(zhuǎn)換能剝離一個對象const或volatile屬性,也就是說允許你對常量進(jìn)行修改。


vs2013里定義頭文件時,不能對cpp文件重命名為.h,屬性不對,編譯不過。


命名空間解決函數(shù)重名和變量重名。


迭代式(增量式)開發(fā)機(jī)制,命名空間提供。
1.可以分段定義;
2.空間變量和函數(shù)不能重名。
3.命名空間一般是公有的。
4.命名空間可以無限嵌套,加多層。
5.命名空間可以別名。
6.匿名命名空間可以編譯,等價于全局變量,局部變量覆蓋全局變量。


namespace?
命名空間可以別名。


默認(rèn)參數(shù)
1.默認(rèn)參數(shù)不賦值時,必須位于最左側(cè),默認(rèn)參數(shù)之間不允許有賦值的參數(shù)。
2.參數(shù)處理,從右向左處理
3.函數(shù)指針調(diào)用時,無法使用默認(rèn)參數(shù),需明確傳參
4.函數(shù)重載沖突時,可以使用函數(shù)指針解決問題


const類型
1.c++是強(qiáng)類型檢測,指針類型不匹配,編譯不過。
2.c++編譯器對const單個變量做了優(yōu)化,直接讀值,讀常量符號表,不訪問內(nèi)存。

3.const數(shù)組,需要讀內(nèi)存


如: const int a = 10; int *p = (int *)&num; *p = 5; cout<<a<<*p<<endl;


自動變量
auto num = 3/2.0;
sizeof(num)= ?;
根據(jù)右側(cè)變量推導(dǎo)左側(cè)存儲空間大小
auto定義變量時,必須初始化


重載函數(shù)
1.參數(shù)個數(shù)有關(guān)
2.參數(shù)類型
3.參數(shù)順序不同
4.與函數(shù)返回值無關(guān)


重載函數(shù)地址無法打印,需轉(zhuǎn)換成函數(shù)指針后再打印。


引用
1.給一個已經(jīng)存在的變量起一個別名,地址相同。
2.引用初始化的各種形式。
3.vs平臺下int *p; int * (&rp)(p);g++平臺下int *&rp(p).
4.引用定義時,需要初始化賦值


結(jié)構(gòu)體內(nèi)部的代碼不占結(jié)構(gòu)體存儲空間。


new和malloc的區(qū)別?經(jīng)測試,在統(tǒng)一存儲空間分配內(nèi)存。




4種類型轉(zhuǎn)換:
1.const_cast
2.dynamic_cast
3.reinterpret_cast
4.static_cast


讀書筆記c++ primer


第十二章


類的構(gòu)造函數(shù)
1.如果類的成員變量是const或引用類型,使用初始化列表是不二選擇。


類內(nèi)部定義的函數(shù)默認(rèn)為inline,在外部定義成員函數(shù)必須指明它們是在類的作用域中。
如:Sales_tem::avg_price的定義使用作用域。


const加在形參表之后,可以將成員函數(shù)聲明為常量:
double avg_price() const;
const成員不能改變其所操作對象的數(shù)據(jù)成員。const必須同時出現(xiàn)在聲明和定義中,若只出現(xiàn)在其中一處,就會出一個編譯時錯誤。


類背后蘊(yùn)涵的基本思想是數(shù)據(jù)抽象和封裝。
數(shù)據(jù)抽象是一種依賴于接口和實現(xiàn)分離的編程技術(shù)。類的設(shè)計者必須關(guān)心類是如何實現(xiàn)的,但使用該類的程序員不必了解這些細(xì)節(jié)。相反使用一個類型的程序員僅需要了解類型的接口,他們可以抽象的考慮該類型做什么而不必考慮該類型如何工作。


如果類是用struct關(guān)鍵字定義的,則在第一個訪問標(biāo)號之前的成員是公有的,
如果類是用class關(guān)鍵字定義的,則這些成員是私有的。


具體類和抽象類型:并非所有類型都必須是抽象的。標(biāo)準(zhǔn)庫中pair類就是一個實用的、設(shè)計良好的具體類而不是抽象類。具體類會暴露而非隱藏其實現(xiàn)細(xì)節(jié)。


好的類設(shè)計者會定義直觀和易用的類接口,而使用者只關(guān)心類中影響他們使用的那部分實現(xiàn)。如果類的實現(xiàn)速度太慢或給類的使用者加上負(fù)擔(dān),則必然引起使用者的關(guān)注。在良好設(shè)計的類中,只有類的設(shè)計者會關(guān)心實現(xiàn)。
在簡單的應(yīng)用程序中,類的使用者和設(shè)計者也許是同一個人。即使在這種情況下,保持角色區(qū)分也是有益的,
設(shè)計類的接口時,設(shè)計者應(yīng)該考慮的是如何方便類的使用;使用類的時候,設(shè)計者就不應(yīng)該考慮類如何工作。


數(shù)據(jù)抽象和封裝提供了兩個重要優(yōu)點:
1.避免類內(nèi)部出現(xiàn)無意的、可能破壞對象狀態(tài)的用戶級錯誤。
2.隨時間推移可以根據(jù)需求改變或缺陷(bug)報告來完善類實現(xiàn),而無須改變用戶級代碼。


類型別名來簡化類
class Screen {
public:
? typedef std::string::size_type index;
private:
? index cursor;
? index height,width;
};
將index的定義放在類的public部分,是因為希望用戶使用這個名字。Screen類的使用者不必了解用string實現(xiàn)的底層細(xì)節(jié)。


成員函數(shù)可被重載,類的成員函數(shù)與普通的非成員函數(shù)以及在其他類中聲明的函數(shù)不相關(guān),也不能重載它們。
兩個重載成員函數(shù)的形參數(shù)量和類型不能完全相同。
例:
class Screen {
public:
? typedef std::string::szie_type index;
? char get() const {return contents[cursor];}
? char get(index ht, index wd) const;
private:
? std::string contents;
? index cursor;
? index height, width;
};


Screen myscreen;
char ch = myscreen.get();
ch = myscreen.get(0,0);


顯式指定inline成員函數(shù),在聲明和定義處指定inline都是合法的。在類的外部定義inline的一個好處是可以使得類比較容易閱讀。
inline成員函數(shù)的定義必須在調(diào)用該函數(shù)的每個源文件中是可見的。不在類定義體內(nèi)定義的inline成員函數(shù),其定義通常應(yīng)放在有類定義的同一頭文件中。


在一個給定的源文件中,一個類只能被定義一次。
如果在多個文件中定義一個類,那么每個文件中的定義必須是完全相同的。
將類定義放在頭文件中,可以保證在每個使用類的文件中以同樣的方式定義類。


聲明一個類而不定義它:
class Screen; ?
這個聲明有時稱為前向聲明,在聲明之后,定義之前,類Screen是一個不完全類型(incompete type),即已知Screen是一個類型,但不知道包含哪些成員。
不完全類型只能以有限方式使用。
不能定義該類型的對象(不知成員,不知開辟多大存儲)。
只能定義指向該類型的指針及引用,或者用于聲明使用該類型作為形參類型或返回類型的函數(shù)。
在創(chuàng)建類的對象之前,必須完整的定義該類。
同樣,在使用引用或指針訪問類的成員之前,必須已經(jīng)定義類。


只有當(dāng)類定義已經(jīng)在前面出現(xiàn)過,數(shù)據(jù)成員才能被指定為該類類型。如果該類型是不完全類型,那么數(shù)據(jù)成員只能是指向該類類型的指針或引用。
class LinkScreen{
? Screen window;
? LinkScreen *next;
? LinkScreen *prev;
};


類的前向聲明一般用來編寫相互依賴的類。


定義對象風(fēng)格:
Sales_item item1; ? //c++風(fēng)格
class Sales_item item1; ? ?//c風(fēng)格


成員函數(shù)不能定義this形參,而是由編譯器隱含的定義。
成員函數(shù)的函數(shù)體可以顯式的使用this指針,但不是必須這么做。如果對類成員的引用沒有限定,編譯器會將這種引用處理成通過this指針的引用。


何時使用this指針?盡管在成員函數(shù)內(nèi)部顯式引用this通常是不必要的,但有一種情況下必須這么做,當(dāng)我們需要將一個對象作為整體引用而不是引用對象的一個成員時。最常見的情況是在這樣的函數(shù)中使用this;該函數(shù)返回對調(diào)用該函數(shù)的對象的引用。
例:myScreen.move(4,0).set('#');
里面含有return (*this);


從const成員函數(shù)返回*this,在普通的非const成員函數(shù)中,this的類型是一個指向類類型的const指針。可以改變this所指向的值,但不能改變this所保存的地址。在const成員函數(shù)中,this的類型是一個指向const類類型對象的const指針。既不能改變this所指向的對象,也不能改變this所保存的地址。
不能從const成員函數(shù)返回指向類對象的普通引用。const成員函數(shù)只能返回*this作為一個const引用。
例:
display為const修飾成員函數(shù),應(yīng)用于長表達(dá)式中。
myScreen.display().set('*');
我們不能在一個const對象上調(diào)用set。


解決辦法,基于const的重載。
class Screen {
public:
? ?const Screen& display() const {};
? ?Screen& display() {};
};


Screen myScreen(5,3);
const Screen blank(5,3);
myScreen.set('#').display(); ?//calls nonconst version
blank.display(); ? ? ? ? ? ? ?//cslls const version




可變數(shù)據(jù)成員,有時我們希望類的數(shù)據(jù)成員(甚至在const成員函數(shù)內(nèi))可以修改。
這可以通過將它們聲明為mutable來實現(xiàn)。
可變數(shù)據(jù)成員永遠(yuǎn)都能為const,甚至當(dāng)他是const對象的成員時也是如此。




形參表和函數(shù)體處于類作用域中,在定義于類外部的成員函數(shù)中,形參表和成員函數(shù)體都出現(xiàn)在成員名之后。這些都是在類作用域中定義,所以可以不用限定而引用其他成員。
例:
char Screen::get(index r, index c) const
{
? index row = r* width;
? return contents[row+c];
}
該函數(shù)用Screen內(nèi)定義的index類型來指定其形參類型。


函數(shù)的返回類型不一定在類作用域中,與形參類型相比,返回類型出現(xiàn)在成員名字前面。
class Scree {
public:
? typedef std::string::size_type index;
? index get_cursor() const;
};
inline Screen::index Screen::get_cursor() const
{
? return cursor;
}


編譯器按照成員聲明在類中出現(xiàn)的次序來處理它們,通常,名字必須在使用之前進(jìn)行定義。而且,一旦一個名字被用作類型名,該名字就不能被重復(fù)定義。


類成員定義中的名字查找:
1.首先檢查成員函數(shù)局部作用域中的聲明。
2.其次檢查對所有類成員的聲明。
3.最后檢查在此成員函數(shù)定義之前的作用域中出現(xiàn)的聲明。


盡管全局對象被屏蔽了,但通過用全局作用域確定操作符來限定名字,仍然可以使用它
void dummy_fcn(index height)
{
? ?cursor = width * ::height; ?//which height? the global one
}


構(gòu)造函數(shù)的的名字于類的名字相同,并且不能指定返回類型。可以沒有形參,也可以有多個形參


構(gòu)造函數(shù)自動執(zhí)行,只要創(chuàng)建該類型的一個對象,編譯器就運(yùn)行一個構(gòu)造函數(shù)。


構(gòu)造函數(shù)不能聲明為const,創(chuàng)建類類型的const對象時,運(yùn)行一個普通的構(gòu)造函數(shù)來初始化該const對象。
例:
class Sales_item {
public:
? Sales_item() const; ? //error
};


const Sales T;


與其他函數(shù)不同,構(gòu)造函數(shù)可以包含一個初始化列表,構(gòu)造函數(shù)可以定義在類的內(nèi)部或外部。
構(gòu)造函數(shù)初始化式只在構(gòu)造函數(shù)的定義中而不是聲明中指定。


不管成員是否在構(gòu)造函數(shù)初始化列表中顯式的初始化,類類型的數(shù)據(jù)成員總是在初始化階段初始化。初始化發(fā)生在計算階段開始之前。
使用構(gòu)造函數(shù)初始化列表的版本初始化數(shù)據(jù)成員,發(fā)生在初始化階段。函數(shù)體內(nèi)對數(shù)據(jù)成員賦值的發(fā)生在函數(shù)計算執(zhí)行階段。


有些成員必須在構(gòu)造函數(shù)初始化列表中進(jìn)行初始化。對于這樣的成員,在構(gòu)造函數(shù)體中對他們賦值不起作用。
沒有默認(rèn)構(gòu)造函數(shù)的類類型的成員,以及const或引用類型的成員,不管是哪種類型,都必須在構(gòu)造函數(shù)初始化列表中進(jìn)行初始化。


按照與成員聲明一致的次序編寫構(gòu)造函數(shù)初始化列表是個好主意。此外,盡可能避免使用成員來初始化其他成員。


類通常應(yīng)定義一個默認(rèn)構(gòu)造函數(shù)。假定有一個NoDefault類,定義了一個接受string實參的構(gòu)造函數(shù),則編譯器不在合成默認(rèn)構(gòu)造函數(shù)。
1.必須通過傳遞一個初始化的string值給NoDefault的構(gòu)造函數(shù)來顯式的初始化NoDefault成員
2.編譯器不再默認(rèn)合成默認(rèn)構(gòu)造函數(shù)。
3.此類型不能用作動態(tài)分配數(shù)據(jù)的元素類型。
4.此類型的靜態(tài)分配數(shù)組必須為每個元素提供一個顯式的初始化式。
5.如果有一個保存NoDefault對象的容器,例如vector,就不能使用接受容器大小而沒有同時提供一個元素初始化式的構(gòu)造函數(shù)。
實際上,如果定義了其他構(gòu)造函數(shù),則提供一個默認(rèn)構(gòu)造函數(shù)幾乎總是對的。


使用構(gòu)造函數(shù):
Sales_item myobj;
Sales_item myobj = Sales_item();
Sales_item myobj();錯誤




隱式類類型轉(zhuǎn)換。可以用單個實參來調(diào)用的構(gòu)造函數(shù)定義了從形參類型到該類類型的一個隱式轉(zhuǎn)換。
class Sales_item {
public:
? Sales_item(const std::string &book=""):
? ? isbn(book), units_sold(0), revenue(0.0) {}
? Sales_item(std::istream &is);
};
string null_book = "999";
item.same_isbn(null_book);
same_isbn本身期待一個Sales_item的參數(shù),此時發(fā)生隱式類型轉(zhuǎn)換,null_book調(diào)用構(gòu)造函數(shù),生成一個新的Sales_ttem對象(臨時對象),被傳遞給了same_sibn。
一旦same_isbn結(jié)束,就不能再訪問此臨時對象,我們構(gòu)造了一個在測試完成后被丟棄的對象,這個行為幾乎肯定是一個錯誤。


抑制由構(gòu)造函數(shù)定義的隱式轉(zhuǎn)換,可以通過將構(gòu)造函數(shù)聲明為explicit,來防止在需要隱式轉(zhuǎn)換的上下文中使用構(gòu)造函數(shù)。
explicit關(guān)鍵字只能用于類內(nèi)部的構(gòu)造函數(shù)聲明上。在類的定義體外部所做的定義上不再重復(fù)它。重復(fù)會報錯。
class Sales_item {
public:
? explicit Sales_item(const std::string &book=""):
? ? isbn(book), units_sold(0), revenue(0.0) {}
? explicit Sales_item(std::istream &is);
};
string null_book = "999";
item.same_isbn(null_book); //此時編譯時會報錯。


為轉(zhuǎn)換而顯式的使用構(gòu)造函數(shù)
item.same_isbn(Sales_item(null_book));


通常,除非有明顯的理由想要定義隱式轉(zhuǎn)換,否則,單形參構(gòu)造函數(shù)應(yīng)該為explicit。


類成員的顯式初始化,這是從c中繼承而來的,有三個重大的缺點:
1.要求類的全體數(shù)據(jù)成員都是public。
2.將初始化每個對象的每個成員的負(fù)擔(dān)放在程序員身上。乏味且易于出錯的,因為容易遺忘初始化式或提供不適當(dāng)?shù)某跏蓟健?
3.如果增加或刪除一個成員,必須找到所有的初始化并正確更新。




友元,在某些情況下,允許特定的非成員函數(shù)訪問一個類的私有成員,同時仍然阻止一般的訪問,這是很方便做到的。
友元機(jī)制允許一個類將對其非公有成員的訪問權(quán)授予指定的函數(shù)或類。友元的聲明關(guān)鍵字friend。
它只能出現(xiàn)在類定義的內(nèi)部,友元的聲明可以出現(xiàn)在類中的任何地方。
通常,將友元聲明成組的放在類定義的開始或結(jié)尾是個好主意。
友元類,友元函數(shù)。
定義友元類時,友元類必須先被定義過。
友元聲明將已命名的類或非成員函數(shù)引入到外圍作用域中。此外,友元函數(shù)可以在類的內(nèi)部定義,該函數(shù)的作用域擴(kuò)展到包圍該類定義的作用域。


static數(shù)據(jù)成員獨立于該類的任意對象而存在,是與類關(guān)聯(lián)的對象。
static成員函數(shù)沒有this形參,它可以直接訪問所屬類的static成員,但不能訪問非static成員。


使用類的static成員的優(yōu)點:
1.static成員的名字在類的作用域中,因此可以避免與其他類的成員或全局對象名字沖突。
2.可以實施封裝,static成員可以是私有成員,而全局變量不可以。
3.通過閱讀程序容易看出static成員是與特定類關(guān)聯(lián)的。


在類的外部定義static成員時,無須重復(fù)指定static保留字,該保留字只出現(xiàn)在類定義體內(nèi)部的聲明處。


static成員函數(shù)沒有this指針,因為它不屬于任何對象的組成部分。


static關(guān)鍵字只能用于類定義體內(nèi)部的聲明中,定義不能標(biāo)示為static。


double Account::interestRate = initRate();
其中interestRate在類中是static聲明修飾,在類外定義時不能再加static,initRate是類中的私有函數(shù),前面已經(jīng)有類作用域的聲明,后面可以直接使用類中的initRate函數(shù)。
static在類中聲明變量,在類外定義變量,存儲空間開辟在數(shù)據(jù)段上。


例外,const static修飾的變量可以在類體中直接賦值。但也需要在類外定義此變量的存儲空間。定義時不需要再賦值。


static成員函數(shù)不與對象綁定,沒有this指針






第十三章


拷貝(復(fù)制)構(gòu)造函數(shù):是一種特殊的構(gòu)造函數(shù),具有單個形參,該形參(常用const修飾)是對該類類型的引用。
當(dāng)定義一個新對象并用一個同類型的對象對它進(jìn)行初始化時,將顯式使用拷貝構(gòu)造函數(shù)。當(dāng)將該類型的對象傳遞給函數(shù)或從函數(shù)返回該類型對象時,將隱式使用拷貝構(gòu)造函數(shù)。


析構(gòu)函數(shù)是構(gòu)造函數(shù)的互補(bǔ):當(dāng)對象超出作用域或動態(tài)分配的對象被刪除時,將自動應(yīng)用析構(gòu)函數(shù)。析構(gòu)函數(shù)可用于釋放對象所獲取的資源。
不管類是否定義了自己的析構(gòu)函數(shù),編譯器都自動執(zhí)行類中非static數(shù)據(jù)成員的析構(gòu)函數(shù)。


與默認(rèn)構(gòu)造函數(shù)一樣,拷貝構(gòu)造函數(shù)可以由編譯器隱式調(diào)用。拷貝構(gòu)造函數(shù)可用于:
1.根據(jù)另一個同類型的對象顯式或隱式初始化一個對象。
2.復(fù)制一個對象,將它作為實參傳給一個函數(shù)。
3.從函數(shù)返回時復(fù)制一個對象。
4.初始化順序容器中的元素。
5.根據(jù)元素初始化式列表初始化數(shù)組元素。


默認(rèn)合成的拷貝構(gòu)造函數(shù),執(zhí)行逐個成員初始化,將新對象初始化為原對象的副本。
數(shù)組是個例外,雖然一般不能直接復(fù)制數(shù)組,但如果一個類具有數(shù)組成員,則合成拷貝構(gòu)造函數(shù)將復(fù)制整個數(shù)組。


因為用于向函數(shù)傳遞對象和從函數(shù)返回對象,該構(gòu)造函數(shù)一般不應(yīng)設(shè)置為explicit。


通常,定義拷貝構(gòu)造函數(shù)最困難的部分在于認(rèn)識到需要拷貝構(gòu)造函數(shù)。只有能認(rèn)識到需要拷貝構(gòu)造函數(shù),定義拷貝構(gòu)造函數(shù)一般非常簡單。




對象創(chuàng)建時用另一個對象初始化賦值,調(diào)用拷貝構(gòu)造函數(shù)。
對象已經(jīng)存在,賦值時,調(diào)用賦值函數(shù)。


如果我們不想實在不想編寫拷貝構(gòu)造函數(shù)和賦值函數(shù),又不允許別人使用編譯器默認(rèn)提供的缺省函數(shù)怎么辦?
只需要將拷貝構(gòu)造函數(shù)和賦值函數(shù)聲明為私有函數(shù),不用編寫代碼。因為編譯器在默認(rèn)調(diào)用這類函數(shù)時,發(fā)現(xiàn)其為私有成員函數(shù),則編譯報錯。




為了防止復(fù)制,類必須顯式聲明其拷貝構(gòu)造函數(shù)為private。
如果拷貝構(gòu)造函數(shù)是私有的,將不允許用戶代碼復(fù)制該類類型的對象,編譯器將拒絕任何進(jìn)行復(fù)制的嘗試。
然而,友元和成員函數(shù)可以進(jìn)行復(fù)制。如果想連友元和成員函數(shù)中的復(fù)制也禁止,就可以聲明一個private拷貝構(gòu)造函數(shù)但不對其定義。
聲明而不定義成員函數(shù)是合法的,但是,使用未定義成員的任何嘗試將導(dǎo)致鏈接失敗。
通過聲明(但不定義)private拷貝構(gòu)造函數(shù),可以禁止任何復(fù)制類類型對象的嘗試:用戶代碼中的復(fù)制嘗試將在編譯時標(biāo)記為錯誤,而成員函數(shù)和友元中的復(fù)制嘗試將在連接時導(dǎo)致錯誤。




一般來說,最好顯式或隱式定義默認(rèn)構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)。只有不存在其它構(gòu)造函數(shù)時才合成默認(rèn)構(gòu)造函數(shù)。如果定義了拷貝構(gòu)造函數(shù),也必須定義默認(rèn)構(gòu)造函數(shù)。


大多數(shù)操作符可以定義為成員函數(shù)或非成員函數(shù)。當(dāng)操作符為成員函數(shù)時,它的第一個操作數(shù)隱式綁定到this指針。因此,賦值操作符接受單個形參,且該形參是同一類類型的對象。右操作數(shù)一般為const引用傳遞。


例如:
Sales_item& ?Sales_item::operator=(const Sales_item &rhs)
{
成員逐個賦值。。。
* 刪除已開辟空間。。。
申請新空間。。。
拷貝。。。
return *this;
}


一般而言,如果類需要拷貝構(gòu)造函數(shù),我們幾乎肯定也需要賦值操作符。


構(gòu)造函數(shù)的一個用途是自動獲取資源。例如,構(gòu)造函數(shù)可以分配一個緩沖區(qū)或打開一個文件,在構(gòu)造函數(shù)中分配了資源之后,需要一個對應(yīng)操作自動回收或釋放資源。析構(gòu)函數(shù)就是這樣的一個特殊函數(shù),它可以完成所需的資源回收,作為類構(gòu)造函數(shù)的補(bǔ)充。




容器中的元素是按逆序撤銷。


如果類需要析構(gòu)函數(shù),則它也需要賦值操作符合拷貝構(gòu)造函數(shù),這是一個有用的經(jīng)驗法則。這個規(guī)則常稱為三法則(rule of three),指的是如果需要析構(gòu)函數(shù),則需要所有這三個復(fù)制控制成員。


合成析構(gòu)函數(shù)按對象創(chuàng)建時的逆序撤銷每個非static成員。


析構(gòu)函數(shù)是個成員函數(shù),它的名字是在類名字之前加上一個~,它沒有返回值,沒有形參。因為不能指定任何形參,所以不能重載析構(gòu)函數(shù)。


析構(gòu)函數(shù)與拷貝構(gòu)造函數(shù)或賦值操作符之間的一個重要區(qū)別是,即使我們編寫了自己的析構(gòu)函數(shù),合成析構(gòu)函數(shù)仍然運(yùn)行。先執(zhí)行自己定義的析構(gòu)函數(shù),再執(zhí)行合成析構(gòu)函數(shù)。
理解:自己的析構(gòu)函數(shù)銷是合成析構(gòu)函數(shù)的一個擴(kuò)展補(bǔ)充,負(fù)責(zé)毀堆資源和關(guān)閉文件,合成析構(gòu)函數(shù)負(fù)責(zé)銷毀普通的數(shù)據(jù)成員。




管理指針成員:
1.指針成員采取常規(guī)指針行為。這樣的類具有指針的所有缺陷但無需特殊的復(fù)制控制。
2.類可以實現(xiàn)所謂的“智能指針”行為。指針?biāo)赶虻膶ο髸r共享的,但類能夠防止懸垂指針。
3.類采取值型行為。指針?biāo)赶虻膶ο髸r唯一的,由每個類對象獨立管理。




智能指針?
懸垂指針:指針指向的存儲空間已被釋放。


第十四章 重載操作符與轉(zhuǎn)換


重載操作符是具有特殊名稱的函數(shù):保留字operator后接需定義的操作符符號。像任意其他函數(shù)一樣,重載操作符具有返回類型和形參表,如下語句:
Sales_item ooperator+(const Sales_itme& , const Sales_item&);


不可重載的操作符
:: .* . ?:
其它的操作符都可以重載。


重載操作符必須具有至少一個類類型或枚舉類型的操作數(shù)。這條規(guī)則強(qiáng)制重載操作符不能重新定義用于內(nèi)置類型對象的操作符的含義。


優(yōu)先級和結(jié)核性是固定的。


不再具備短路求值特性。


類成員與非成員,大多數(shù)重載操作符可以定義為普通非成員函數(shù)或類的成員函數(shù)。
作為類成員的重載函數(shù),其形參看起來比操作數(shù)數(shù)目少1.作為成員函數(shù)的操作符有一個隱含的this形參,限定為第一個操作數(shù)。


一般將算數(shù)和關(guān)系操作符定義為非成員函數(shù),而將賦值操作定義為成員。




重載操作符的設(shè)計。
1.不要重載具有內(nèi)置含義的操作符。重載逗號,取地址,邏輯與、邏輯或等操作符通常不是好做法。這些操作符具有有用的內(nèi)置含義,如果我們定義了自己的版本,就不能再使用這些內(nèi)置含義。
2.大多數(shù)操作符對類對象沒有意義。一般相等測試應(yīng)使用operator==;輸入輸出使用移位操作符;測試對象是否為空使用operator!。
3.如果一個類有算數(shù)操作符或位操作符,那么提供相應(yīng)的復(fù)合賦值操作符一般是個好做法。
4.相等和關(guān)系操作符,關(guān)聯(lián)容器應(yīng)該定義== 、<操作符,許多算法假定這些操作符存在。如果定義了==,則應(yīng)定義!=,如果定義了<,則應(yīng)定義(>,>=,<,<=)。
5.選擇成員或非成員實現(xiàn)。
原則:
=、下標(biāo)[]、調(diào)用()和成員訪問->等操作符必須定義為成員函數(shù),否則編譯報錯。
復(fù)合賦值操作符通常定義為類的成員。與賦值不同的是,不一定非得這樣做,編譯不報錯。
改變對象狀態(tài)或與給定類型緊密聯(lián)系的其它一些操作符,如自增自減和解引用,通常定義為成員函數(shù)。
對稱的操作符,如算數(shù)操作符、相等操作符、關(guān)系操作符和位操作符,最好定義為普通非成員函數(shù)。


當(dāng)一個重載操作符的定義不明顯時,給操作取一個名字更好。對于很少用的操作,使用命名函數(shù)通常也比用操作符更好。如果不是普通操作,沒有必要為簡潔而使用操作符。


輸入輸出IO操作必須為非成員函數(shù)。否則,左操作數(shù)只能是該類類型的對象:
Sales_item Item;
item << cout;
這個用法與為其他類型定義的輸出操作符的正常使用方式相反。


輸出操作符通常所做格式化應(yīng)盡量少,并且不應(yīng)該輸出換行符。




輸入操作符>>的重載,必須處理錯誤和文件結(jié)束的可能性。
設(shè)計輸入操作時,如果可能,要確定錯誤恢復(fù)措施,這很重要。


如果既定義了算數(shù)操作符又定義了相關(guān)復(fù)合賦值操作符的類,一般應(yīng)用復(fù)合賦值實現(xiàn)算數(shù)操作符。如+,用+=去實現(xiàn),比其它方式更簡單且更有效,不必創(chuàng)建和銷毀一個臨時來保存+的結(jié)果。


賦值必須返回對*this的引用。


類定義下標(biāo)操作符時,一般需要定義兩個版本:一個為非const成員并返回引用,另一個為const成員并返回const引用。


為了支持指針類型,例如迭代器,c++語言允許重載解引用操作符*和箭頭->。
->必須定義為類成員函數(shù)。*不要求定義為成員,但將它作為成員一般也是正確的。


區(qū)別前綴++和后綴++,后綴式操作符函數(shù)接受一個額外的(無用的)int型形參。前綴不需要參數(shù)。




調(diào)用函數(shù)()重載???




第十五章 ?面向?qū)ο缶幊?


面向?qū)ο缶幊袒谌齻€基本概念:數(shù)據(jù)抽象、繼承和動態(tài)綁定。在c++中用類進(jìn)行數(shù)據(jù)抽象,用類派生從一個類繼承另一個類:派生類繼承基類的成員。動態(tài)綁定使編譯器能夠在運(yùn)行時決定使用基類中定義的函數(shù)還是派生類中定義的函數(shù)。


多態(tài)性緊用于通過繼承而相關(guān)的類型的引用或指針。


在c++中,基類必須指出希望派生類重定義哪些函數(shù),定義為virtual的函數(shù)是基類期待派生類重新定義的,基類希望派生類繼承的函數(shù)不能定義為虛函數(shù)。


動態(tài)綁定,我們能夠編寫程序使用繼承層次中任意類型的對象,無須關(guān)心對象的具體類型。使用這些類的程序無須區(qū)分函數(shù)是在基類還是在派生類中定義的。


通過基類的引用(或指針)調(diào)用虛函數(shù)時,發(fā)生動態(tài)綁定。引用(或指針)既可以指向基類對象也可以指向派生類對象,這一事實是動態(tài)綁定的關(guān)鍵。用引用(或指針)調(diào)用的虛函數(shù)在運(yùn)行時確定,被調(diào)用的函數(shù)是引用(或指針)所指對象的實際類型所定義的。


protected成員不能被類的用戶訪問,可以被該類的派生類訪問。
派生類只能通過派生類對象訪問其基類的protected成員,派生類對其基類類型對象的protected成員沒有特殊訪問權(quán)限。


為了定義派生類,使用類派生列表指定基類。派生列表指定了一個或多個基類,具有如下形式:
class classname: access-lable ?base-class
這里access-lable是public、protected、private,base-class是已定義的類的名字。類派生列表可以指定多個基類。繼承單個基類最為常見。


盡管不是必須這樣做,派生類一般會重定義所繼承的虛函數(shù)。如果派生類沒有重定義某個虛函數(shù),則使用基類中定義的版本。


派生類型必須對想要重定義的每個繼承成員進(jìn)行聲明。


派生類中虛函數(shù)的聲明必須與基類中的定義方式完全匹配,但有一個例外:返回對基類型的引用(或指針)的虛函數(shù)。派生類中的虛函數(shù)可以返回基類函數(shù)所返回類型的派生類的引用(或指針)。
如:Item_base類可以定義返回Item_base*的虛函數(shù),如果這樣,派生類Bulk_item類中定義的實例可以定義返回Item_base*或Bulk_item*。


一旦函數(shù)在基類中聲明為虛函數(shù),它就一直未虛函數(shù),派生類無法改變該函數(shù)為虛函數(shù)這一事實。派生類重定義虛函數(shù)時,可以使用virtual保留字,但不是必須這樣做。


c++語言不要求編譯器將對象的基類部分和派生類部分連續(xù)排列。


派生類中的函數(shù)可以使用基類的成員。


用作基類的類必須是已定義的。


從效果來說,最底層的派生類對象包含其每個直接基類和間接基類的子對象。


c++中的函數(shù)調(diào)用默認(rèn)不使用動態(tài)綁定。要觸發(fā)動態(tài)綁定,必須滿足兩個條件:
1.只有指定為虛函數(shù)的成員函數(shù)才能進(jìn)行動態(tài)綁定,成員函數(shù)默認(rèn)為非虛函數(shù),非虛函數(shù)不進(jìn)行動態(tài)綁定;
2.必須通過基類類型的引用或指針進(jìn)行函數(shù)調(diào)用。


引用和指針的靜態(tài)類型與動態(tài)類型可以不同,這是c++用以支持多態(tài)性的基石。
通過基類引用或指針調(diào)用基類中定義的函數(shù)時,我們并不知道執(zhí)行函數(shù)的對象的確切類型,執(zhí)行函數(shù)的對象可能是基類型的,也可能是派生類型的。
如果調(diào)用非虛函數(shù),則無論實際對象是什么類型,都執(zhí)行基類類型所定義的函數(shù)。
如果調(diào)用虛函數(shù),則直到運(yùn)行時才能確定調(diào)用哪個函數(shù),運(yùn)行的虛函數(shù)是引用所綁定的或是指針?biāo)赶虻膶ο笏鶎兕愋投x的版本。


在編譯時確定非virtual調(diào)用。


覆蓋虛函數(shù)機(jī)制。在某些情況下,希望覆蓋虛函數(shù)機(jī)制并強(qiáng)制函數(shù)調(diào)用使用虛函數(shù)的特定版本,這時可以使用域操作符:
例:
Item_base *basep = &derived; //derived是派生類的實例對象
double d = basep->Item_base::net_price(43);
只有成員函數(shù)中的代碼才應(yīng)該使用作用域操作符覆蓋虛函數(shù)機(jī)制。


派生類虛函數(shù)調(diào)用基類版本時,必須顯式使用作用域操作符。如果派生類函數(shù)忽略了這樣做,則函數(shù)調(diào)用會在運(yùn)行時確定并且將是一個自身調(diào)用,從而導(dǎo)致無窮遞歸。


虛函數(shù)與默認(rèn)實參,虛函數(shù)也可以有默認(rèn)實參。通常,如果有用在給定調(diào)用中的默認(rèn)實參值,該值將在編譯時確定。如果一個調(diào)用省略了具有默認(rèn)值的實參,則所用的值由調(diào)用函數(shù)的類型定義,與對象的動態(tài)類型無關(guān)。通過基類的引用或指針調(diào)用虛函數(shù)時,默認(rèn)實參為基類虛函數(shù)聲明中指定的值,如果通過派生類的指針或引用調(diào)用虛函數(shù),則默認(rèn)實參是派生類的版本中聲明的值。
在同一虛函數(shù)的基類版本和派生類版本中使用不同的默認(rèn)實參幾乎一定會引起麻煩。
如果通過基類的引用或指針調(diào)用虛函數(shù),但實際執(zhí)行的是派生類中定義的版本,這時可能會出現(xiàn)問題。


公用、私有和受保護(hù)的繼承。
每個類控制它所定義的成員的訪問。派生類可以進(jìn)一步限制但不能放松對所繼承的成員的訪問。


如果是公有繼承public,基類成員保持自己的訪問級別。
如果是受保護(hù)繼承protected,基類的public和protected成員在派生類中為protected成員。
如果是私有繼承private,基類的所有成員在派生類中為private成員。


無論派生列表中是什么訪問標(biāo)號,所有派生類內(nèi)部對基類成員具有相同的訪問。派生類訪問標(biāo)號將控制派生類用戶對從基類繼承而來的成員的訪問。


使用private或protected派生的類不繼承基類的接口,相反,這些派生通常稱為實現(xiàn)繼承。派生類在實現(xiàn)中使用被繼承類但繼承基類的部分并未成為其接口的一部分。
迄今為止,最常見的繼承形式是public。


派生類可以恢復(fù)繼承成員的訪問級別,但不能使訪問級別比基類中原來指定的更嚴(yán)格或更寬松。使用using聲明訪問基類中的名字。


友元關(guān)系不能繼承。基類的友元對派生類的成員沒有特殊訪問權(quán)限。如果基類被授予友元關(guān)系,則只有基類具有特殊訪問權(quán)限,該基類的派生類不能訪問授予友元關(guān)系的類。


如果基類定義了static成員,則整個繼承層次中只有一個這樣的成員。


每個派生類對象包含一個基類部分,所以存在從派生類型引用到基類類型引用的自動轉(zhuǎn)換。
沒有從基類引用(或指針)到派生類引用(或指針)的(自動)轉(zhuǎn)換。




派生類構(gòu)造函數(shù),受繼承關(guān)系影響,每個派生類構(gòu)造函數(shù)除了初始化自己的數(shù)據(jù)成員之外,還要初始化基類。


構(gòu)造函數(shù)初始化列表為類的基類和成員提供初始值,它并不指定初始化的執(zhí)行次序。首先初始化基類,然后根據(jù)聲明次序初始化派生類的成員。


只能初始化直接基類。直接基類就是在派生列表中指定的類。


重構(gòu)包括重新定義類層次。為了適應(yīng)應(yīng)用程序的需要而重新設(shè)計類以便增加新函數(shù)或處理其他改變時,最有可能需要進(jìn)行重構(gòu),重構(gòu)常見在面向?qū)ο髴?yīng)用程序中非常常見。


如果析構(gòu)函數(shù)為虛函數(shù),那么通過指針調(diào)用時,運(yùn)行哪個析構(gòu)函數(shù)將因指針?biāo)笇ο箢愋偷牟煌煌?


即使析構(gòu)函數(shù)沒有工作要做,繼承層次的根類也應(yīng)該定義一個虛析構(gòu)函數(shù)。


構(gòu)造函數(shù)不是虛函數(shù)。構(gòu)造函數(shù)是在對象完全構(gòu)造之前運(yùn)行的,在析構(gòu)函數(shù)運(yùn)行的時候,對象的動態(tài)類型還不完整。




將類的賦值操作符設(shè)為虛函數(shù)很可能令人混淆,而且不會有什么用處。


與基類成員同名的派生類成員將屏蔽對基類成員的直接訪問。使用作用域操作符訪問被屏蔽的成員。


在派生類中使用同一名字的成員函數(shù),其行為與數(shù)據(jù)成員一樣,即使函數(shù)原型不同,基類成員也會被屏蔽。


如果派生類重定義了重載成員,則通過派生類型只能訪問派生類中重定義的那些成員。
如果派生類想通過自身類型使用所有的重載版本,則派生類必須要么重定義所有重載版本,要么一個也不重定義。
否則使用using聲明???




純虛函數(shù),在函數(shù)形參表后面寫上=0.
含有(或繼承)一個或多個純虛函數(shù)的類是抽象類。除了作為抽象基類的派生類的對象的組成部分,不能創(chuàng)建抽象類的對象。




第十六章 模板與泛型編程


面向?qū)ο缶幊讨械亩鄳B(tài)性在運(yùn)行時應(yīng)用于存在繼承關(guān)系的類。我們能夠編寫使用這些類的代碼,忽略基類與派生類之間類型上的差異。
只要使用基類的引用或指針,基類類型或派生類類型的對象就可以使用相同的代碼。


運(yùn)行時多態(tài)性,編譯時多態(tài)性。


在泛型編程中,我們所編寫的類和函數(shù)能夠多態(tài)的用于跨越編譯時不相關(guān)的類型。一個類或一個函數(shù)可以用來操作多種類型的對象。


c++中模板是泛型編程的基礎(chǔ)。


模板定義以關(guān)鍵字template開始,后接模板形參表,模板形參表是用尖括號括住的一個或多個模板形參的列表,形參之間以逗號分隔。
模板形參表不能為空。
template <typename T>
int compare(const T &v1, const T &v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}


模板形參可以是表示類型的類型形參,也可以是表示常量表達(dá)式的非類型形參。


使用函數(shù)模板時,編譯器會推斷哪個模板實參綁定到模板形參。
一旦編譯器確定了實際的模板實參,就稱它實例化了函數(shù)模板的一個實例。
推導(dǎo)出實際模板實參后,編譯器使用實參代替相應(yīng)的模板形參產(chǎn)生并編譯該版本的函數(shù)。
編譯器承擔(dān)了為我們使用每種類型而編寫函數(shù)的單調(diào)工作。




inline函數(shù)模板,需放在模板形參表之后,返回類型之前,不能放在template之前。


每個模板類型形參前必須帶上關(guān)鍵字class或typename,每個非類型形參前面必須帶上類型名字,省略關(guān)鍵字或類型說明符是錯誤的。
例如:
template <typename T, U> T calc(const T&, const U&);


類型形參由關(guān)鍵字class或typename后接說明符構(gòu)成。這兩個關(guān)鍵字具有相同的含義,都指出后面所接的名字表示一個類型。
typename代替關(guān)鍵字class更直觀,畢竟,可以使用內(nèi)置類型作為實際的類型形參。
模板類型形參可以用于指定返回類型或函數(shù)形參類型,以及在函數(shù)體中用于變量聲明或強(qiáng)制類型轉(zhuǎn)換。


通過在成員名前加上關(guān)鍵字typename作為前綴,可以告訴編譯器將成員當(dāng)做類型。
例:
template <class Parm, class U>
Parm fcn(Parm* array, U value)
{
typename Parm::size_type *p; ? //類型or成員?
}


非類型模板形參。在調(diào)用非類型形參將用值代替,值的類型在模板形參表中指定。
例:
template <class T, size_t N> void array_init(T (&parm)[N])
{
for (size_t i = 0; i != N; ++i)
parm[i] = 0;
}




通過將形參設(shè)為const引用,就可以使用不允許復(fù)制的類型。大多數(shù)類型(包括內(nèi)置類型,除IO類型之外)都允許復(fù)制。但是也有不允許復(fù)制的類類型。將形參設(shè)為const引用,保證這種類型可以用于compare函數(shù),而且,如果有比較大的對象調(diào)用compare,則這個設(shè)計還可以運(yùn)行的更快。




模板是一個藍(lán)圖,它本身不是類或函數(shù)。編譯器用模板產(chǎn)生指定的類或函數(shù)的特定類型版本。產(chǎn)生模板的特定類型實例的過程稱為實例化。


類模板的每次實例化都會產(chǎn)生一個獨立的類類型。與其他任意實例類型沒有關(guān)系。


類模板的形參是必須的。
Queue qs; 錯誤
Queue<int> qi;
Queue<string> qs;
Queue不是類型


使用函數(shù)模板時,編譯器通常會為我們推斷模板實參:


int main()
{
compare(1,0);
compare(3.14, 2.7);
return 0;
}




多個類型形參的實參必須完全匹配。如果推斷類型不匹配,則調(diào)用會出錯。


一般而言,不會轉(zhuǎn)換實參以匹配已有的實例化,相反,會產(chǎn)生新的實例。除了產(chǎn)生實例之外,編譯器只會執(zhí)行兩種轉(zhuǎn)換:
1.const轉(zhuǎn)換,接受const引用或const指針的函數(shù)可以分別用非const對象的引用或指針來調(diào)用,無須產(chǎn)生新的實例化。
2.數(shù)組或函數(shù)到指針的轉(zhuǎn)換,如果模板形參不是引用類型,則對數(shù)組或函數(shù)類型的實參應(yīng)用常規(guī)指針轉(zhuǎn)換。


模板實參推斷與函數(shù)指針,可以使用函數(shù)模板對函數(shù)指針進(jìn)行初始化或賦值,編譯器使用指針的類型實例化具有適當(dāng)模板實參的模板版本。
例如:
template <typename T> int compare(const &T, const &T);
int (*pf1)(const int &, const int &) = compare;




Queue類實現(xiàn)
template <class Type> class Queue {
public:
Queue() : head(0), tail(0) { }
Queue(const Queue &Q) : head(0),tail(0) {copy_elems(Q);}
Queue& operator=(const Queue&);
~Queue() {destroy();}
Type &front() {return head->item;}
const Type &front() const {return head->item;}
void push(const Type &);
void pop();
bool empty() const { return head == 0; }
private:
QueueItem<Type> *head;
QueueItem<Type> *tail;
void destroy();
void copy_elems(const Queue&);
};


通常,當(dāng)使用類模板的名字時候,必須指定模板形參。這一規(guī)則有個例外,在類本身的作用域內(nèi)部,可以使用類模板的非限定名。
如Queue是Queue<Type>的縮寫。


類模板成員函數(shù)的定義具有如下形式:
1.必須以關(guān)鍵字template開頭,后接類的模板形參表。
2.必須指出它是哪個類的成員。
3.類名必須包含其模板形參。
如:template <class T> ret-type Queue<T>::member-name


如:
template <class Tyte> void Queue<Type>::destroy()
{
while (!empty())
pop();
}


非類型模板實參必須是編譯時常量表達(dá)式。
template <int hi, int wid> class Screen {};
Screen<24, 80>hp2621;
hi的模板實參是24,wid的模板實參是80.




類模板中的友元聲明:
1.普通非模板類或函數(shù)的友元聲明,每一種都聲明了一個或多個實體的友元關(guān)系。
2.類模板或函數(shù)模板的友元聲明,授予對友元所有實例的訪問權(quán)。
3.只授予對類模板或函數(shù)模板的特定實例的訪問權(quán)的友元聲明。


模板特化
函數(shù)模板特化:
1.關(guān)鍵字template后面接一對空的尖括號<>;
2.再接模板名和一對尖括號,尖括號中指定這個特化定義的模板形參;
3.函數(shù)形參表。
4.函數(shù)體。


處理C風(fēng)格的字符串,如:
template <> int compare<const char *>(const char * const &v1, const char* const &v2)
{
return strcmp(v1,v2);
}



















總結(jié)

以上是生活随笔為你收集整理的c++primer读书笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。