GeekBand C++ Week2 Notes

Week2 Notes

A.三大函數(shù):拷貝構(gòu)造,拷貝賦值,析構(gòu)

string class這個不是標(biāo)準(zhǔn)庫里的string,標(biāo)準(zhǔn)庫里的太復(fù)雜了。

首先也要有防衛(wèi)式聲明言疗。Ifndef define endif

測試代碼

int main(){

? ? strings1();

? ? string s2(“hello”);

? ? string s3(s1);//拷貝

? ? cout << s3 << endl;//操作符重載

? ? s3 = s2;//賦值動作

? ? cout << s3<< endl;

}

上面的賦值和拷貝的不同是第一個s3是第一次出現(xiàn),第二個s3是賦值颂砸。所以第一個是拷貝構(gòu)造噪奄,第二個叫拷貝賦值死姚。如果你沒有寫,編譯器會自動給你一套勤篮,是一位一位地拷貝知允。那我們還要自己寫一份嗎?要考慮編譯器給的這一套夠不夠用叙谨,比如復(fù)數(shù)只要給實(shí)部虛部就夠用温鸽,但是這種帶指針的如果還使用編譯器給的拷貝構(gòu)造和拷貝賦值,就不夠用手负,深拷貝和淺拷貝涤垫??竟终?蝠猬?

Class string{

Public:

? ? String(constchar* cstr = 0);

? ? String(conststring& str);

? ? String&operator = (const string& str);

? ? ~string();

? ? char*get_C_str() const {return m_data};

private:

? ? char*m_data;

}

字符串的指針要寫在private里,我們要自己寫一套拷貝统捶,第一個函數(shù)函數(shù)名稱和類相同榆芦,所以是構(gòu)造函數(shù),第二個函數(shù)名字也一樣喘鸟,也是構(gòu)造函數(shù)匆绣,但它的接收類型是自己,所以是拷貝構(gòu)造什黑。第三個操作符=的輸入也是string崎淳,是拷貝賦值,注意愕把,只要你的類帶著指針拣凹,你一定要寫出這兩個拷貝函數(shù)!:藁怼O怠!

第四個波浪線開頭的是析構(gòu)函數(shù)橘蜜。當(dāng)它死亡的時候析構(gòu)函數(shù)就會被調(diào)用菊匿,新增加的234被成為big three,三個特殊函數(shù)扮匠。

第五個復(fù)習(xí)加的const是因為直接返回data不改變所以要加捧请。

Ctor和dtor(構(gòu)造函數(shù)和析構(gòu)函數(shù))

一個字符串多長有兩種想法,一種是不知道長度但最后有結(jié)束符號棒搜,另一種是沒有結(jié)束符號,但多一個lenth活箕,長度力麸。C和c++用的是有結(jié)束符號的設(shè)計方法。

Inline

String::string(const char* cstr = 0){

If(cstr){

? ? M_data = new char[strlen(cstr) + 1];

? ? Strcpy(m_data, cstr);

}

else{

? ? m_data = new char[1];

? ? *m_data = ‘\0’;

}

}

inline

string::~string(){

? ? delete[]m_Data;

}

{

? ? stirngs1(),

? ? strings2(“hello”);

? ? string*p = new string(“hello”);

? ? delete p;

}

傳進(jìn)來先判斷是不是空字符串,如果是空的分配一個字符克蚂,為結(jié)束符闺鲸,如果不為空,分配的空間大小是傳進(jìn)來的長度還要加上一個1埃叭,結(jié)束符號\0摸恍,分配完了之后,再用strcpy拷貝進(jìn)我們的m_data里赤屋。加入傳進(jìn)來的是hello的話立镶,長度就是5加1,結(jié)束符號类早,面對字符串都要想到結(jié)束符號媚媒,新創(chuàng)建的對象就擁有內(nèi)容了。

對應(yīng)于構(gòu)造函數(shù)涩僻,有下面的析構(gòu)函數(shù)缭召,做的事情是關(guān)門清理,clean up,在之前講過的復(fù)數(shù)時逆日,不需要清理嵌巷,如果你的class里有指針,多半要用動態(tài)分配室抽,new晴竞,在使用動態(tài)分配的時候,我們要用析構(gòu)函數(shù)把動態(tài)分配的空間釋放掉狠半,否則會發(fā)生內(nèi)存泄漏噩死。

String *p = new string(“hello”);

Delete p;

動態(tài)分配后再調(diào)用。

在這個作用域里頭有三個字符串神年,離開的時候要調(diào)用三次析構(gòu)函數(shù)已维。

Class with pointer members必須有copy ctr和copy op=

因為如果不這么做,編譯器自己做的是一個位一個位的拷貝已日,對于b= a來說垛耳,a的內(nèi)容只有一個指針,如果不這么做飘千,b = a就會把b里的指針也指向a指針指向的內(nèi)容堂鲜,這對于兩個內(nèi)容都很危險,對于b的內(nèi)容來說护奈,他指向的內(nèi)容還在可是沒有指針指向他缔莲,會發(fā)生內(nèi)存泄漏,memory leak.對于a來說霉旗,兩個指針指向他也很危險痴奏,一個改變了另一個也改變蛀骇,所以這個叫淺拷貝,我們寫的函數(shù)是為了深拷貝读拆。

Copy ctor拷貝構(gòu)造函數(shù)

Inline

String::string(const string& str){

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

? ? Strcpy(m_Data,str.mdata);

}

{

? ? string s1(“hello”);

? ? string s2(s1);

}

在對m_data賦值的時候直接取另一個object的private擅憔,(兄弟之間互為friend)

在把指針拷過來的同時把內(nèi)容也拷過來,叫深拷貝檐晕,只把指針拷貝過來叫淺拷貝暑诸。

Copy assignment operator拷貝賦值函數(shù)

為了把右邊的東西賦值給左邊,本來兩邊都有東西辟灰,需要先把左邊清空个榕,分配一塊和右邊一樣大的空間,再把右邊拷貝過來伞矩。

Inline

String& string::operator = (conststring& str){

? ? If(this== &str)

? ? ? ? Return*this;//檢測自我賦值

? ? Delete[]m_Data;

? ? M_Data= new char[strlen(str.m_Data) + 1]

? ? Strcpy(m_data,str.m_data);

? ? Return*this;

}

{

string s1(“hello”);

string s2(s1);

s2 = s1;

}

自我賦值的檢測判斷條件是兩個指針相比笛洛,自我賦值如果不寫,好像也不會出錯乃坤,只是效率高低嗎苛让,不是,如果不寫自我賦值檢測湿诊,有可能會出錯狱杰。當(dāng)左右兩邊一樣的時候,賦值的第一步是殺掉左邊的m_data厅须,在做第二個動作的時候string已經(jīng)不見了仿畸,會出錯。所以自我賦值不只是為了效率朗和。

B.堆棧與內(nèi)存管理

Output函數(shù)错沽,是不是可以加在自己的rectangle類里?

#include

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

? ? os<< str.get_c_str();

? ? returnos;

}

stack和heap

函數(shù)本身會形成一個stack用來防止它接收到的參數(shù)眶拉,以及返回地址千埃。

在函數(shù)本體內(nèi)的聲明的任何變量,其所使用的內(nèi)存塊都取自上述stack忆植。

System heap是指操作系統(tǒng)所提供的一塊global的內(nèi)存空間放可,程序可以動態(tài)分配從某種獲得若干區(qū)塊〕可以在程序的任何地方動態(tài)獲得耀里,并有責(zé)任區(qū)釋放他。

Class Complex();

{

? ? complexc1(c1,c2);

? ? complex*p= new complex(3);

}

C1叫做aotu object拾氓,stackobject;

Static complex c2(1, 2);

C2是static object,聲明在作用域結(jié)束之后依然存在冯挎,它的析構(gòu)函數(shù)不會再大括號結(jié)束的時候被調(diào)用,會在整個程序結(jié)束之后被調(diào)用痪枫。

還有一種對象叫全局對象织堂,寫在任何大括號之外的叫做全局對象叠艳,global object,聲明在整個程序結(jié)束之后才消失奶陈,也可以視為一種static object..

{

? ? complex*p = new Complex;

}

上面的程序會發(fā)生內(nèi)存泄漏易阳,在作用域結(jié)束后指針就死亡了,但指針指的對象還存在吃粒,就會發(fā)生memory leak;

new:先分配memory,再調(diào)用ctor;

complex *pc = new complex(1, 2);

編譯器會*pc;

void * mem = operator

new(sizeof(complex));//分配內(nèi)存,里面調(diào)用malloc

pc = static_cast(mem);

pc->complex::complex(1,2);

delete:先調(diào)用dtor,再釋放memory

在復(fù)數(shù)的時候我們沒有寫析構(gòu)函數(shù)潦俺,寫了也是白寫,馬上就要死亡了徐勃。

String ps = new string(“hello”);

Delete ps;

String::~string(ps);//析構(gòu)函數(shù)

Operator delete(ps);//釋放內(nèi)存

使用malloc和free到底分配多少內(nèi)存事示。

Comple:兩個double是8個字節(jié),在調(diào)試模式下上面會多出32個字節(jié)僻肖,下面會多出4個字節(jié)肖爵,上下還有cookie,是4個字節(jié)臀脏。

一共有8+(32+4)+4*2 = 52

在vc下面分配的內(nèi)存塊一定是16字節(jié)的倍數(shù)劝堪,所以分配64個字節(jié)。

在非調(diào)試模式底下揉稚,一個復(fù)數(shù)的大小要去掉灰色的秒啦,所以是8+4*2 = 16個字節(jié)。上下cookie是用來記錄整個的大小搀玖,因為在釋放的時候只給一個指針余境,要知道釋放的內(nèi)存大小cookie head記錄將大小的最后一位記為1.因為大小是16字節(jié)的倍數(shù),所以最后一位肯定是1.

String內(nèi)含一個指針灌诅,分配的內(nèi)存大小為:

4+(32+4)+(4*2) = 48是16的倍數(shù)芳来,在非調(diào)試模式下,大小為4+4*2 = 12猜拾,16

如果分配的是數(shù)組array會怎么樣即舌?

Array new要搭配array delete不然會出錯。

Complex*p = new complex[3];

(8*3) + (32+4)+(4*2)+4 = 72

給80

非調(diào)試模式下关带,(8*3) + (4*2) + 4 = 36給48

如果array new沒有搭配array delete侥涵,會造成內(nèi)存泄漏。

String *p = new string[3];

Delete[] p;

寫不寫中括號都不影響指針這一整塊的刪除宋雏,因為分配的內(nèi)存的大小就記錄在cookie上芜飘,問題出在沒加中括號只調(diào)用了一次dtor,只有寫了中括號編譯器才知道你下面是數(shù)組磨总,會喚起三次dtor嗦明,這三個dtor負(fù)責(zé)把各自動態(tài)分配的內(nèi)存殺掉。

C.復(fù)習(xí)String類的實(shí)現(xiàn)

動態(tài)分配的內(nèi)存塊蚪燕。(memory block)

下面來寫一個字符串的class娶牌,編程實(shí)例:

class String{

public:

? ? String(constchar* cstr = 0);

? ? String(constString& str);

? ? String&operator = (const String& str);

? ? ~String();

? ? char*get_c_str() const {return m_data;}

private:

? ? char* m_data;

};

構(gòu)造函數(shù)和析構(gòu)函數(shù)

inline

String::String(const char* cstr = 0){

If(cstr){

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

? ? strcpy(m_data,cstr);

}

else{//未設(shè)定初值

? ? m_data = new char[1];

? ? *m_data = ‘\0’;

}

}

inline

String::~String(){

? ? delete[]m_data;

}

由于上面是用array new,所以下面用array delete奔浅。

拷貝構(gòu)造函數(shù)copy cstr

inline

String::String(const String& str){

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

? ? strcpy(m_data,str.m_data);

}

copy assignment operator拷貝賦值函數(shù)

inline

String& String::operator=(constString& str){

? ? If(this== &str)

? ? ? ? return*this;

? ? delete[] m_data;

? ? m_data = newchar[strlen(str.m_data) + 1];

? ? strcpy(m_data,str.m_data);

? ? return *this;

}

返回類型不是void,因為當(dāng)連續(xù)=時可能會出錯。

拷貝賦值要去檢驗是否是自我賦值诗良,如果是自我賦值汹桦,就可以直接返回。如果沒有做這種檢查鉴裹,不只是效率的問題舞骆,是正誤的問題。在這里const String& str中的&符號和this == &str有不同的意義径荔,前面是傳引用督禽,后面是取地址。前面是出現(xiàn)在typename的后面总处,叫引用狈惫,后面的是出現(xiàn)在object的前面,叫取地址鹦马,得到的是指針胧谈。

D.擴(kuò)展補(bǔ)充,類模板菠红,函數(shù)模板及其他

對象經(jīng)典有帶指針的和不帶指針的第岖。有很多細(xì)節(jié)需要補(bǔ)充。

進(jìn)一步補(bǔ)充:static靜態(tài)

當(dāng)完成基于對象后要做面向?qū)ο笫运荩诓煌念悆?nèi)做時要知道this pointer

不管是數(shù)據(jù)還是函數(shù)前面都可以加static

complex c1, c2, c3;

cout << c1.real();

cout << c2.real();

c1調(diào)用real函數(shù)從另一個角度看就是

complex c1, c2, c3;

cout << complex::real(&c1);

cout << complex::real(&c2);

在沒有導(dǎo)入static的時候蔑滓,函數(shù)只有一份,但是它要來處理很多個對象遇绞,一定要有人告訴它你要處理誰键袱,靠的就是this pointer,通過這個指針找到它要處理的對象在哪里摹闽。成員函數(shù)有一個this pointer但是我們不能寫進(jìn)去蹄咖,這是編譯器自動會幫我們寫。

加了靜態(tài)static后它和對象就脫離了付鹿,他在內(nèi)存中有單獨(dú)一部分區(qū)域澜汤,靜態(tài)數(shù)。

靜態(tài)函數(shù)的身份和一般的成員函數(shù)一樣在內(nèi)存中只有一份舵匾。靜態(tài)的數(shù)據(jù)只有一份俊抵,什么時候會使用呢?比如在設(shè)計一個銀行的賬戶體系坐梯,有一百萬個人來開戶徽诲,我們需要設(shè)計一百萬個人的對象,但利率需要一個同一個東西,一百萬個人同一個利率谎替,此時我們需要將利率設(shè)為靜態(tài)偷溺,在內(nèi)存中只有一份,靜態(tài)函數(shù)的特征和一般成員函數(shù)不同在它沒有this pointer,這樣它不能去訪問去處理钱贯,那它有什么用挫掏,靜態(tài)函數(shù)要處理數(shù)據(jù)的話它只能處理靜態(tài)的數(shù)據(jù)。例子:

class account{

public:

? ? staticdouble m_rate;

? ? staticvoid set_rate(const double& x) {m_rate = x;}

};

double account::m_rate = 8.0;

int main(){

? ? account::set_rate(5.0);

? ? accounta;

? ? a.set_rate(7.0);

}

靜態(tài)的數(shù)據(jù)一般在類外面加定義,要不要給初值喷舀?都可以砍濒。

靜態(tài)函數(shù)只能處理靜態(tài)的數(shù)據(jù)淋肾,調(diào)用static函數(shù)的方式有兩種硫麻,一種是通過object調(diào)用,第二種是通過class name來調(diào)用樊卓。

進(jìn)一步補(bǔ)充:把ctors放在private區(qū)

Singleton

Class A{

Public:

StaticA& getInstance{return a;}

Setup(){…}

Private:

? ? A();

? ? A(const A&rhs);

? ? Static A a;

}

A::getInstance().setup();

這個寫法還不是最完美拿愧,雖然寫好了a,但當(dāng)外界都不需要用到碌尔,a仍然存在浇辜,更好的寫法:

Meyers Singleton

class A{

public:

? ? staticA& get Instance();

? ? setup(){…}

private:

? ? A();

? ? A(constA& rhs);

};

A& A::getInstance(){

? ? staticA a;

? ? returna;

}

只有調(diào)用過getInstance后單例才開始存在。

進(jìn)一步補(bǔ)充:cout

重載了各種數(shù)據(jù)的<<操作符

進(jìn)一步補(bǔ)充:class template唾戚,類模板

template

把double都換成T柳洋,使用時

complex c1(2.5,1.5);

complex c2(2, 6);

模板可能會造成代碼的膨脹,以上會產(chǎn)生兩份相似的代碼叹坦,但這個是必要的熊镣。

進(jìn)一步補(bǔ)充:function template函數(shù)模板

stone r1(2, 3), r2(3, 3), r3;

r3 = min(r1, r2);

template

inline

const T& min(const T& a, constT& b){

? ? returnb < a? b:a;

}

class stone{

public:

stone(intw, int h, int we)

:_w(w), _h(h), _weight(we) {}

booloperator < (const stone& rhs) const

{return_weight < rhs.weight;}

private:

int_w, _h, _weight;

};

進(jìn)一步補(bǔ)充:namespace

namespace std{

}

using directive

#include

using namespace std;

int main(){

? ? cin<< …;

? ? cout<< …;

? ? return0;

}

using declaration

using std::cout;

int main(){

? ? std::cin<< …;

? ? cout<< …;

? ? return0;

}

#include

int main(){

? ? std::cin<< …;

? ? std::cout<< …;

? ? return 0;

}


在這周的作業(yè)中我遇到了一個類公有繼承了另一個類,并且在子類中含有一個指針指向一個類募书,在寫這個類的拷貝構(gòu)造函數(shù)時绪囱,需要將輸入對象中指針下面的值賦給this對象中的指針,這里由于需要將輸入對象leftup指針中的值取出莹捡,我在類point中寫了兩個取數(shù)據(jù)的公有方法叫g(shù)etX和getY鬼吵,對于賦值,我采用的方法是借用構(gòu)造函數(shù)對新對象中的數(shù)據(jù)進(jìn)行賦值篮赢。另一方面齿椅,對于類rectangle的父類中no的處理,我的理解是no用于記錄每一個生成的shape的編號启泣,這樣就需要一個靜態(tài)的全局?jǐn)?shù)據(jù)count用于記錄一共擁有的shape數(shù)涣脚,并在每生成一個新的shape時將對應(yīng)的編號分給no,這個過程我在shape的構(gòu)造函數(shù)中實(shí)現(xiàn),對應(yīng)的种远,在shape的析構(gòu)函數(shù)中涩澡,我將count值減一,保證shape總數(shù)和現(xiàn)有的shape對象個數(shù)相同。在測試中妙同,我先測試了拷貝構(gòu)造和拷貝賦值的正常使用另外在刪除其中一個對象后射富,再新生成一個rectangle,它對應(yīng)的no應(yīng)該是正確的粥帚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末店煞,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子扛稽,更是在濱河造成了極大的恐慌物延,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件费尽,死亡現(xiàn)場離奇詭異赠群,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)旱幼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門查描,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柏卤,你說我怎么就攤上這事冬三。” “怎么了缘缚?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵勾笆,是天一觀的道長。 經(jīng)常有香客問我桥滨,道長窝爪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任该园,我火速辦了婚禮酸舍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘里初。我一直安慰自己啃勉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布双妨。 她就那樣靜靜地躺著淮阐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刁品。 梳的紋絲不亂的頭發(fā)上泣特,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音挑随,去河邊找鬼状您。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的膏孟。 我是一名探鬼主播眯分,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼柒桑!你這毒婦竟也來了弊决?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤魁淳,失蹤者是張志新(化名)和其女友劉穎飘诗,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體界逛,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昆稿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了仇奶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片貌嫡。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖该溯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情别惦,我是刑警寧澤狈茉,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站掸掸,受9級特大地震影響氯庆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扰付,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一堤撵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧羽莺,春花似錦实昨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至刁卜,卻和暖如春志电,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蛔趴。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工挑辆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓鱼蝉,卻偏偏與公主長得像茉继,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蚀乔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容