GeekBand C++ 第二周

7.三大函數(shù):拷貝構(gòu)造税迷,拷貝賦值骏融,析構(gòu)

String s3(s1);//拷貝構(gòu)造函數(shù)(s3剛剛出現(xiàn))
String s4 = s1;//這種情況也是拷貝構(gòu)造(雖然用的'='南誊,但是S4剛剛出現(xiàn))
s3 = s2;//拷貝賦值(s3已經(jīng)出現(xiàn))

無指針的類颓帝,不需要寫拷貝構(gòu)造和拷貝賦值瞻离。類內(nèi)帶指針饺汹,一定要寫拷貝構(gòu)造和拷貝賦值肴裙,不能用編譯器自動生成的宅静。

  • 構(gòu)造函數(shù)章蚣,參數(shù)類型是自身類型,則為拷貝構(gòu)造函數(shù)姨夹。
  • 拷貝賦值纤垂,重載=操作符,參數(shù)類型是自身類型磷账。
  • 和構(gòu)造函數(shù)名稱相同峭沦,前面加~,是析構(gòu)函數(shù)逃糟,當(dāng)類的對象死亡的時候吼鱼,析構(gòu)函數(shù)會被調(diào)用蓬豁。

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

inline
String::String(const char* cstr = 0)
{
  if(cstr){
    m_data = new char[strlen(cstr) + 1];
    strcpt(m_data, cstr);
  }else{
    m_data = new char[1];
    *m_data = '\0';
  }
}
  • c語言 字符串,以'\0'結(jié)尾菇肃,字符串長度地粪,以'\0'標(biāo)記來計算。另一種在字符串的前面有長度標(biāo)示琐谤,后面沒有結(jié)束符蟆技。
  • 字符串長度為0,也要用一個字符笑跛,來保存'\0'付魔,為了析構(gòu)函數(shù)統(tǒng)一析構(gòu),一個字符用 new char[1] 來創(chuàng)建飞蹂。
  • 當(dāng)字符串長度不為0,則用strlen()計算出長度+1翻屈,用來保存最后的'\0'陈哑。

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

inline
String::~String(){
    delete[] m_data;//array delete配合 array new
}
  • 析構(gòu)函數(shù)的作用,清理伸眶,cleanup惊窖。
  • 離開作用域時要釋放內(nèi)存。

big three
class with pointer members 必須有 copy ctor 和 copy operator=厘贼。如果沒有使用界酒,則極易造成內(nèi)存泄露,且兩個類中的指針指向同一塊內(nèi)存嘴秸,改變A毁欣,則B也被改變。

copy ctor
inline
構(gòu)造函數(shù)岳掐,接受參數(shù)類型為本身凭疮,則為拷貝構(gòu)造函數(shù)。
深拷貝:首先創(chuàng)造足夠的空間串述,然后把內(nèi)容拷貝到新的對象中执解。
淺拷貝:則會造成兩個‘人’在‘看’同一個東西。

copy assignment operator 拷貝賦值函數(shù)
右邊的對象拷貝的左邊纲酗,左右兩邊原本都有內(nèi)容

  • 1.首先要清空左邊衰腌。
  • 2.然后在左邊分配和右邊一樣的空間。
  • 3.再把右邊的內(nèi)容拷貝到左邊觅赊。
  • 4.特別要注意右蕊,要檢測自我賦值,如果不檢測茉兰,則自身在拷貝之前就被干掉了尤泽,造成內(nèi)存錯誤。如果檢測到自我賦值,則直接返回坯约。不單單是為了效率熊咽,更是為了安全。

8.堆闹丐,棧與內(nèi)存管理

8.1.Stack和Heap

Stack

是存在于某作用于(scope)的一塊內(nèi)存空間(memory space)横殴。調(diào)用函數(shù)時,函數(shù)本身即會形成一個stack用來放置它所接收的參數(shù)卿拴,以及返回地址衫仑,以及l(fā)ocal object。

Heap

是由操作系統(tǒng)提供的一塊global內(nèi)存空間堕花,程序可以動態(tài)分配從其中獲得若干區(qū)塊文狱,new出來的,必須手動delete掉缘挽。

stack objects的生命周期

stack object瞄崇,即為local object,又稱為 auto object壕曼,生命在作用域結(jié)束之后就結(jié)束了苏研。對象的析構(gòu)函數(shù)會被調(diào)用。

static local objects的生命周期

static object腮郊,其生命在作用域結(jié)束后仍然存在摹蘑,直到整個程序結(jié)束。

global objects的生命周期

任何寫在大括號之外的對象轧飞,其生命在main函數(shù)之前就存在衅鹿,在程序結(jié)束之后才結(jié)束,作用域是“整個程序”踪少。

heap objects的生命周期

new得到的對象塘安,在使用完畢之后要delete掉。如果沒有delete援奢,則會造成內(nèi)存泄露兼犯。

8.2 new delete

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

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

編譯器把new分解為三個動作:

  • 1.分配內(nèi)存 void* mem = operator new(sizeof(Complex)); // 內(nèi)部調(diào)用malloc(n);
  • 2.轉(zhuǎn)型pc = static_cast<Complex*>(mem);
  • 3.構(gòu)造函數(shù)pc->Complex::Complex(1,2); // 其實(shí)際參數(shù)列表為 Complex::Complex(pc, 1, 2);

delete:先調(diào)用dtor集漾,在釋放memory

String *ps = new String("Hello");
...
delete ps;

編譯器把delete分解為兩個動作:

  • 1.析構(gòu)函數(shù) String::~String(ps); // 析構(gòu)函數(shù)會delete掉String類內(nèi)部動態(tài)分配的空間
  • 2.釋放內(nèi)存 operator delete(ps); // 其內(nèi)部調(diào)用free(ps)切黔;

共計兩次delete。

8.3 動態(tài)分配所得的內(nèi)存塊(memory block)

1.動態(tài)分配所得的對象

1.Complex *pc = new Complex(1, 2);

在debug模式下具篇,class的前面有32字節(jié)纬霞,后面有4字節(jié),前后cooky各4字節(jié)驱显,cooky為0x41诗芜,共計:

8+(32+4)+(4*2)=52 -> 64

在release模式下瞳抓,class本身8個字節(jié),前后cooky各4個字節(jié)伏恐,cooky為0x11孩哑,共計:

8+(4*2)=16 -> 16

上下cooky的作用,記錄整塊給你的大小翠桦。采用16進(jìn)制横蜒,如果是0,則代表系統(tǒng)回收销凑,如果是1丛晌,則代表系統(tǒng)給出。在vs的編譯器下斗幼,給的內(nèi)存的大小為16的倍數(shù)澎蛛,所以cooky在16進(jìn)制時最后一位一直為0,所以可以用來標(biāo)記內(nèi)存的方向蜕窿。

2.String *ps = new String("Hello");

在debug模式下瓶竭,cooky為0x31,共計:

4+(32+4)+(4*2)=48 -> 48

在release模式下渠羞,cooky為0x11,共計:

4+(4*2)=12 -> 16

2.動態(tài)分配所得的 array

array new 要搭配 array delete智哀,不然會出錯次询。

1.Complex *p = new Complex[3];

在debug模式下,cooky為0x51瓷叫,共計:

(8*3)+(32+4)+(4*2)+4=72 -> 80

在release模式下屯吊,cooky為0x31,共計:

(8*3)+(4*2)+4=36 -> 48

2.String *p = new String[3];

在debug模式下摹菠,cooky為0x41盒卸,共計:

(4*3)+(32+4)+(4*2)+4=60 -> 64

在release模式下,cooky為0x31次氨,共計:

(4*3)+(4*2)+4=24 -> 32

3.array new 一定要搭配 array delete

String *p = new String[3];
...
delete[] p; // 調(diào)用3次dtor
memory 解釋
21h cooky記錄內(nèi)存大小
3 數(shù)組的大小
String[0] 調(diào)用dtor
String[1] 調(diào)用dtor
String[2] 調(diào)用dtor
000000000(pad) 填充內(nèi)存
21h cooky記錄內(nèi)存大小
String *p = new String[3];
...
delete p; // 調(diào)用1次dtor
memory 解釋
21h cooky記錄內(nèi)存大小
3 數(shù)組的大小
String[0] 調(diào)用dtor
String[1] 未調(diào)用dtor
String[2] 未調(diào)用dtor
000000000(pad) 填充內(nèi)存
21h cooky記錄內(nèi)存大小

對比發(fā)現(xiàn)蔽介,整塊的內(nèi)存并沒有發(fā)生內(nèi)存泄露,因?yàn)檎麎K內(nèi)存的大小記錄在cooky當(dāng)中煮寡。如果沒有寫array delete而寫的是delete虹蓄,編譯器不知道下面有幾個對象,因此只有第一個也就是String[0]調(diào)用了dtor幸撕,其余的對象并沒有調(diào)用dtor薇组。當(dāng)調(diào)用玩dtor之后,再釋放掉整塊的內(nèi)存坐儿。由此可以發(fā)現(xiàn)律胀,如果此時的例子是Complex類的話宋光,那么由于類內(nèi)部沒有指針,所以即使用array new炭菌,但沒用array delete罪佳,也不會產(chǎn)生內(nèi)存泄露。

但是在寫代碼時娃兽,我們應(yīng)養(yǎng)成好的編碼習(xí)慣菇民,array new 一定要搭配 array delete。

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

  • 1.防衛(wèi)式聲明
    #ifndef _MYSTRING_
    #define _MYSTRING_
    class String{
    ...
    };
    #endif
    
  • 2.如何去定義內(nèi)部變量
    • 放數(shù)組投储,但是數(shù)組的大小無法確定第练。
    • 放指針,當(dāng)需要時玛荞,動態(tài)分配(new)字符串的大小,在32位的系統(tǒng)中娇掏,一個指針是4byte,放在private中。
      char *m_data;
      
  • 3.ctor勋眯,放在public婴梧;
    String(const char* cstr = 0);
    
    • 只是接受字符串,不會改變客蹋,要加上const塞蹭。
  • 4.class with point member:
    • copy ctor:
      String(cosnt String& str);
      
    • copy assignment operator:
      String& operator=(const String& str);
      
      • 對于copy ctor 和copy assignment函數(shù),不會改變被拷貝的對象讶坯,所以要加上const番电。
      • 返回拷貝的對象,因?yàn)榉祷亟Y(jié)果不是放在local object中辆琅,目標(biāo)本來存在漱办,因此使用return by reference。
    • dtor:
      ~String();
      
  • 5.輔助函數(shù)
    • 為了能夠cout字符串婉烟,因此需要一個函數(shù)能夠取出String類中的字符串娩井。
      char* get_c_str() cosnt { return m_data; }
      
    • 因?yàn)楹瘮?shù)簡單,直接使用inline的方式似袁。因?yàn)椴粫淖儗ο蟮某蓡T變量洞辣,因此需要加上const。
  • 6.ctor叔营,copy ctor屋彪,copy assignment 都不需要加const
  • 7.ctor和dtor
    • ctor
      inline //建議編譯器
      String::String(cosnt char* cstr = 0){
          if(cstr){
              //以下兩個函數(shù)為C函數(shù),需要相應(yīng)頭文件
              m_data = new char[strlen(cstr) + 1];
              strcpy(m_data, cstr);
          }else{
              m_data = new char[1];
              *m_data = '\0';
          }
      }
      
    • dtor
      inline //建議編譯器
      String::~String(){
          delete[] m_data;
          //由于ctor使用了array new绒尊,因此這里也要使用array delete   
      }
      
    • copy ctor
      inline
      String::String(cosnt String& str){
          m_data = new char[strlen(str.m_data) + 1];
          strcpy(m_data, str.m_data);
      }
      
      • inline只是建議畜挥,不能inline的話也沒關(guān)系。
    • copy assignment operator
      inline
      String& String::operator= (cosnt String& str){//此時&為reference
          //首先判斷是否自我賦值,不單單是效率問題婴谱,更是正確與否的問題蟹但。
          if(this == &str)//此時的&為取地址
              return *this;
              
          //在進(jìn)行拷貝賦值
          delete[] m_data;
          m_data = new char[strlen(str.m_data) + 1];
          strcpy(m_data, str.m_data);
          return *this;
          //傳出去值不在乎用何種方式接受
      }
      
      • 關(guān)于返回值躯泰,當(dāng)不需要連續(xù)賦值時,則不需要返回值华糖,當(dāng)需要連續(xù)賦值時麦向,則需要返回值。
      String s1, s2, s3("Hello");
      s1 = s2 = s3;
      

10.擴(kuò)展補(bǔ)充:類模板客叉,函數(shù)模板及其他

1.static

Class complex{
public:
    double real() const{
    return this->re;
    }
private:
    double re;
    double im;
};
  • C++的習(xí)慣寫法
complex c1, c2, c3;
cout << c1.real();
cout << c2.real();
  • 從C的角度考慮完成同上功能的寫法
complex c1, c2 ,c3;
cout << complex::real(&c1);
cout << complex::real(&c2);

同一個函數(shù)real(),之所以能處理不同對象的數(shù)據(jù)诵竭,靠的就是this point。

static data members 在內(nèi)存的單獨(dú)位置兼搏,有且只有一份卵慰。

static member functions
同樣在內(nèi)存的單獨(dú)位置,函數(shù)本身也僅僅只有一份佛呻。但是跟一般的成員函數(shù)有個區(qū)別裳朋,它沒有this point。它只能去處理靜態(tài)的數(shù)據(jù)吓著。

靜態(tài)的變量鲤嫡,在類的內(nèi)部只是生命,需要在類外部定義绑莺。類型 類名稱::變量名(初始化操作);

調(diào)用靜態(tài)函數(shù)的方法有兩種:

  • 1)通過object調(diào)用暖眼。但是this指針不會被作為參數(shù)傳入函數(shù)中。
  • 2)通過class name調(diào)用纺裁。

單例模式罢荡,把ctors放在private區(qū)域。

class A{
public:
    static A& getInstance(){ return a; }
    setup()
private:
    A();
    A(const A& rhs);
    static A a;
    ...
};
A::getInstance().setup();

外界想要使用a对扶,只能用過:getInstance()獲得。

meyers Singleton:

class A{
public:
    static A& getInstance()惭缰;
    setup()
private:
    A();
    A(const A& rhs);
    ...
};

A& A::getInstance(){
    static A a;
    return a;
}

當(dāng)外界不需要使用這個類時浪南,a不會被創(chuàng)建,只有當(dāng)外界需要使用這個類漱受,調(diào)用了getInstance()函數(shù)络凿,a才會被創(chuàng)建。

2.關(guān)于cout

查看標(biāo)準(zhǔn)庫ostream代碼昂羡,重載了很多的operator<<

ostream

3.class template絮记,類模板

在類的前面加上:

template<typename T>
class complex{
...
};

用T吧具體的類代替,當(dāng)實(shí)際使用時虐先,根據(jù)實(shí)際的需要怨愤,生成具體的類代碼。

{
    complex<double> c1(2.5, 1.5); //用double代替T生成一份類的代碼
    complex<double> c2(2, 6); //用int代替T生成一份類的代碼
    
}

4.function template蛹批,函數(shù)模板

template<class T>
inline
const T& min(const T& a, const T& b){
    retuen a < b ? a : b;
}

所有比較大小都是這么操作撰洗,因此可以使用函數(shù)模板篮愉。實(shí)際比較時如何去比較,則依賴于需要比較大小的類差导。類似于這種函數(shù)试躏,稱之為算法。

{
    complex c1(1, 2), c2(3, 4), c3;
    c3 = min(c1, c2);//當(dāng)調(diào)用min()函數(shù)時设褐,編譯器會進(jìn)行實(shí)參推導(dǎo)(argument deduction)颠蕴,不必再使用的時候指定類型。
}

5.namespace

避免全局變量助析,函數(shù)以及類的同名犀被,則需要namespace,如果每個人自己頂一個namespace貌笨,則不會造成沖突弱判。

  • using directive
    using namespace std;
    {
      cin >> ...;
      cout << ...;
    }
    
  • using declaration
    using std::cout;
    {
      std::cin >> ...;
      cout << ...;
    }
    
  • not use
    {
      std::cin >> ...;
      std::cout << ...;
    }
    

6.更多的細(xì)節(jié),仍需努力

仍需努力的部分
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锥惋,一起剝皮案震驚了整個濱河市昌腰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌膀跌,老刑警劉巖遭商,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捅伤,居然都是意外死亡劫流,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門丛忆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祠汇,“玉大人,你說我怎么就攤上這事熄诡】珊埽” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵凰浮,是天一觀的道長我抠。 經(jīng)常有香客問我,道長袜茧,這世上最難降的妖魔是什么菜拓? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮笛厦,結(jié)果婚禮上纳鼎,老公的妹妹穿的比我還像新娘。我一直安慰自己裳凸,他們只是感情好喷橙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布啥么。 她就那樣靜靜地躺著,像睡著了一般贰逾。 火紅的嫁衣襯著肌膚如雪悬荣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天疙剑,我揣著相機(jī)與錄音氯迂,去河邊找鬼。 笑死言缤,一個胖子當(dāng)著我的面吹牛嚼蚀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播管挟,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼轿曙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了僻孝?” 一聲冷哼從身側(cè)響起导帝,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎穿铆,沒想到半個月后您单,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荞雏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年虐秦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凤优。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡悦陋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出筑辨,到底是詐尸還是另有隱情叨恨,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布挖垛,位于F島的核電站,受9級特大地震影響秉颗,放射性物質(zhì)發(fā)生泄漏痢毒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一蚕甥、第九天 我趴在偏房一處隱蔽的房頂上張望哪替。 院中可真熱鬧,春花似錦菇怀、人聲如沸凭舶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帅霜。三九已至匆背,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間身冀,已是汗流浹背钝尸。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留搂根,地道東北人珍促。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像剩愧,于是被迫代替她去往敵國和親猪叙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

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