博覽網/boolan-C++面向對象高級編程(上)-第2周筆記

2.1 拷貝構造函數 拷貝賦值運算符 析構函數

2.1.1拷貝構造函數

第一個參數必須是自身類類型的引用,且任何額外參數都有默認值。(為什么必須是引用?見后解釋)

合成拷貝構造函數:如果我們沒有為一個類定義拷貝構造函數,則編譯器會為我們定義一個。同合成的默認構造函數不同的是,即使我們定義了其他構造函數,編譯器也會為我們合成一個拷貝構造函數。(一旦自己定義了構造函數,則不會合成默認構造函數)

拷貝初始化與直接初始化

直接初始化:要求編譯器使用普通的函數匹配來選擇與我們提供的參數最匹配的構造函數。

拷貝初始化:要求編譯器將右側運算對象拷貝到正在創建的對象中,如果需要的話,還要進行類型轉換。

string dots(10, '.');? ? ? ? ? ? ? //直接初始化

string s(dots);? ? ? ? ? ? ? ? ? ? //直接初始化

string s2 = dots;? ? ? ? ? ? ? ? ? //拷貝初始化

string null_book = "9-999-8999";? ? //拷貝初始化

string nines = string(100, '9');? ? //拷貝初始化

使用‘=’號的是拷貝初始化,不使用等號的是直接初始化。

拷貝初始化發生在以下情況

1. 用 = 定義變量時發生。

2. 將一個對象作為實參傳遞給一個非引用類型的形參。

3. 從一個返回類型為非引用類型的函數返回一個對象。

4. 用花括號列表初始化一個數組中的元素或一個聚合類中的成員。(聚合類是指沒有用戶定義的構造函數,沒有私有和保護的非靜態數據成員,沒有基類,沒有虛函數)。

拷貝構造函數第一個參數必須是引用原因:由于拷貝構造函數被用來初始化非引用類類型的參數。如果其自身參數不是引用類型,則調用永遠也不會成功——為了調用拷貝構造函數,我們必須拷貝它的實參,但為了拷貝實參,我們又必須調用拷貝構造函數,如此無限循環。

2.1.2 拷貝賦值運算符

與類控制其對象如何初始化一樣,類也可以控制器對象如何賦值:

Sales_data trans, accum;

trans = accum;? //使用Sales_data的拷貝賦值運算符

與拷貝構造函數一樣,如果類未定義自己的拷貝賦值運算符,編譯器也會為它合成一個。

重載賦值運算符

重載運算符本質上是函數,其名字由operator關鍵字后接表示要定義的運算符的符號組成。因此,賦值運算符就是一個名為operator=的函數。類似于任何其他函數,運算符函數也有一個返回類型和一個參數列表。

如果是一個運算符是一個成員函數,其左側運算對象就綁定到隱式的this參數。對于一個二元運算符,例如賦值運算符,其右側運算對象作為顯式參數傳遞。

拷貝賦值運算符接受一個與其類相同類型的參數:

class Foo{

public:

Foo& operator=(const Foo&);? //賦值運算符

//...

};

為了與內置類型的賦值保持一致,賦值運算符通常返回一個指向其左側運算對象的引用。注意,標準庫通常要求保存在容器中的類型要有其賦值運算符,且其返回值是左側運算對象的引用。

2.1.3 析構函數

析構函是類的一個成員函數,名字由波浪號接類名構成。它沒有返回值,也不接受參數:

class Foo{

public:

~Foo();? //析構函數

//...

};

由于析構函數不接受參數,因此它不能被重載。對于一個給定類,只會由唯一一個析構函數。

在一個構造函數中,成員的初始化時在函數體執行之前完成的,且按照它們在類中出現的順序進行初始化。在一個析構函數中,首先執行函數體,然后銷毀成員。成員按初始化順序的逆序進行銷毀。

無論何時一個對象被銷毀,就會自動調用其析構函數:

1. 變量在離開其作用域時被銷毀

2. 當一個對象被銷毀時,其成員被銷毀

3. 容器(無論是標準容器還是數組)被銷毀時,其元素被銷毀

4. 對于動態分配的對象,當對指向它的指針應用delete運算符時被銷毀

5. 對于臨時對象,當創建它的完整表達式結束時被銷毀

2.2 堆 棧和內存管理

a) 棧:內存由編譯器在需要時自動分配和釋放。通常用來存儲局部變量和函數參數。(為運行函數而分配的局部變量、函數參數、返回地址等存放在棧區)。棧運算分配內置于處理器的指令集中,效率很高,但是分配的內存容量有限。

b) 堆:內存使用new進行分配,使用delete或delete[]釋放。如果未能對內存進行正確的釋放,會造成內存泄漏。但在程序結束時,會由操作系統自動回收。

c) 自由存儲區:使用malloc進行分配,使用free進行回收。和堆類似。

d) 全局/靜態存儲區:全局變量和靜態變量被分配到同一塊內存中,C語言中區分初始化和未初始化的,C++中不再區分了。(全局變量、靜態數據、常量存放在全局數據區)

e) 常量存儲區:存儲常量,不允許被修改。

這里,在一些資料中是這樣定義C++內存分配的,可編程內存在基本上分為這樣的幾大部分:靜態存儲區、堆區和棧區。他們的功能不同,對他們使用方式也就不同。

a)靜態存儲區:內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。它主要存放靜態數據、全局數據和常量。

b)棧區:在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。

c)堆區:亦稱動態內存分配。程序在運行的時候用malloc或new申請任意大小的內存,程序員自己負責在適當的時候用free或 delete釋放內存。動態內存的生存期可以由我們決定,如果我們不釋放內存,程序將在最后才釋放掉動態內存。 但是,良好的編程習慣是:如果某動態內存不再使用,需要將其釋放掉,否則,我們認為發生了內存泄漏現象。

2.3 string類的實現

String里涉及動態內存的管理,默認的拷貝構造函數在運行時只會進行淺復制,即只復制內存區域的指針,會造成兩個對象指向同一塊內存區域的現象。如果一個對象銷毀或改變了該內存區域,會造成另一個對象運行或者邏輯上出錯。這時就要求自己實現這些函數進行深復制,即不止復制指針,需要連同內存的內容一起復制。

//代碼參考C++primer.//String類的實現,

#include using namespace std;

class String{

friend ostream& operator<< (ostream&,String&);

public:

String(const char* str=NULL);??????????????? //賦值構造兼默認構造函數(char)

String(const String &other);???????????????? //賦值構造函數(String)

String& operator=(const String&other);?????? //operator=

String operator+(const String &other)const;? //operator+

bool operator==(const String&);????????????? //operator==

char& operator[](unsigned int);????????????? //operator[]

size_t size(){return strlen(m_data);};

~String(void) {delete[] m_data;}

private:

char *m_data;

};

inline String::String(const char* str)

{

if (!str) m_data=0;

else

{

m_data = new char[strlen(str)+1];

strcpy(m_data,str);

}

}

inline String::String(const String& other)

{

if(!other.m_data) m_data=0;

else

{

m_data=new char[strlen(other.m_data)+1];

strcpy(m_data,other.m_data);

}

}

inline String& String::operator=(const String& other)

{

if (this!=&other)

{

delete[] m_data;

if(!other.m_data) m_data=0;

else

{

m_data = new char[strlen(other.m_data)+1];

strcpy(m_data,other.m_data);

}

}

return *this;

}

inline String String::operator+(const String &other)const

{

String newString;

if(!other.m_data)

newString = *this;

else if(!m_data)

newString = other;

else

{

newString.m_data = new char[strlen(m_data)+strlen(other.m_data)+1];

strcpy(newString.m_data,m_data);

strcat(newString.m_data,other.m_data);

}

return newString;

}

inline bool String::operator==(const String &s)

{

if ( strlen(s.m_data) != strlen(m_data) )

return false;

return strcmp(m_data,s.m_data)?false:true;

}

inline char& String::operator[](unsigned int e)

{

if (e>=0&&e<=strlen(m_data))

return m_data[e];

}

ostream& operator<<(ostream& os,String& str)

{

os << str.m_data;

return os;

}

void main()

{

String str1="Hello!";

String str2="Teacher!";

String str3 = str1+str2;

cout<<str3<<"/n"<<str3.size()<<endl;

}

2.4 函數模板 類模板

2.4.1函數模板

重載函數通常用于對不同的數據類型執行相似的操作,不同數據類型的程序邏輯可能有所不同。如果每種數據類型的程序邏輯和操作都相同,則可以使用函數模板來更緊湊、更方便地實現函數重載。

就本質而言,定義一個函數模板就定義了一群重載函數。

所有的函數模板定義都從關鍵字template開始,后接它的模板參數表,列表位于一對尖括號(<和>)中。表示類型的每個模板參數,其前面都必須帶關鍵字class或template(二者可以互換),例如:

template?<?typename?T>???或

template?<?class??ElementType>??或

template?<?typename??BorderType,?typename??FillType>

函數模板定義中的類型模型參數,用于指定函數實參的類型。函數的返回類型或聲明函數內部的變量。除此之外,函數定義與其他任何函數定義的形式相同。

2.4.2 類模板

類模板類模板稱為參數化類型,因為它需要一個或者多個類型參數來指定如何定制一個“泛型類”模板,以形成一個類模板特殊化。為了產生各種類模板特殊化,只需編寫一個類模板定義。每次需要一個額外的類模板特殊化時,只需要使用一種清晰、簡單的方法,編譯器就能為所需要的類模板特殊化編寫源代碼。

Demo:例如一個Stack類模板,可以是程序中創建許多Stack類的基礎,如:"Stack of? double"、"Stack of? int"、"Stack of? char"、"Stack of? Employee"等等創建類模板Stackstack.h :[cpp] view plain copy #ifndef STACK_H? #define STACK_H? ? ? template//指定一個使用參數類型T的類模板定義

class?stack

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內容