(Boolan) C++面向?qū)ο蟾呒?jí)編程(二)

之前寫到了關(guān)于不帶有指針的class的設(shè)計(jì)思路和注意事項(xiàng)弄屡,但是對(duì)于C/C++語言來說寨闹,還有一個(gè)非常重要的概念就是指針础嫡,為什么將指針作為class設(shè)計(jì)的分界點(diǎn)呢氛谜?那么掏觉,之前我講了一個(gè)關(guān)于貓ヽ(=?ω?=)丿的故事,那么今天我再來講一個(gè)關(guān)于國(guó)王和寶箱的故事吧 <( ‵□′)───C<─___-)||值漫。

關(guān)于 指 針(鑰匙)的一個(gè)小故事

語文老師教導(dǎo)澳腹,講故事需要先說明時(shí)間地點(diǎn)人物發(fā)生了啥:
話說很久很久以前(發(fā)現(xiàn)詞語好匱乏、杨何、酱塔、),在遙遠(yuǎn)的國(guó)度有一個(gè)國(guó)王(突然想起了和尚)危虱,他有一件十分心愛的具備魔法的寶物羊娃,每次祭奠、儀式都會(huì)用到這個(gè)寶物的魔力埃跷。

而這個(gè)寶物由十個(gè)部件組成蕊玷,每次只有將十個(gè)部件全部拼接在一起才會(huì)發(fā)揮他的魔力(少了任何一塊都不行(¬_¬))。由于國(guó)王愛這個(gè)寶物愛的死去活來的弥雹,為了防止他過度沉溺在寶物的喜愛中無法自拔而耽誤朝政((=垃帅。=)其實(shí)我想說,到底啥寶物這么有吸引力剪勿。贸诚。。故事設(shè)定。酱固。械念。(▼皿▼#) ....就這樣吧)。

國(guó)王的爹爹(居然沒死╮(╯_╰)╭)將這個(gè)的碎片分別鎖在了十個(gè)寶箱里面(ヾ(???ゞ)這個(gè)故事背景我實(shí)在快編不下去了.......)运悲。龄减。。而每個(gè)箱子都對(duì)應(yīng)一把獨(dú)特的鑰匙(廢話扇苞。欺殿。寄纵。鳖敷。。一把鑰匙開一把鎖━━( ̄ー ̄*|||━━)程拭,特殊之處在于
***這 個(gè) 鑰 匙 不 但 可 以 打 開 箱 子 還 可 以 記 錄 箱 子 在 哪 里 , ****定踱。

而國(guó)王的爹爹把這十把鑰匙分別交給了十個(gè)大臣來保管,也就是只有當(dāng)國(guó)王召集了十個(gè)大臣以后才能夠一睹寶物的真面目恃鞋。

ps: (● ̄(?) ̄●)背景就交代在這吧崖媚,雖然不合邏輯的地方很多,但是為了說清楚這個(gè)事恤浪,也就當(dāng)作合理吧畅哑。

**** 那 么 問 題 來 了 !K伞荠呐!****( ⊙ o ⊙ )
等到國(guó)王的爹爹死了,國(guó)王也長(zhǎng)大了不少砂客,決心要做一個(gè)好的國(guó)王泥张,已經(jīng)不那么迷戀那個(gè)寶物了。但是每次在使用寶物的時(shí)候鞠值,國(guó)王發(fā)現(xiàn)實(shí)在太麻煩了媚创,每次都要十個(gè)大臣全來了才能取出寶物的每個(gè)部件。有好幾次急用的時(shí)候(,,#?Д?)彤恶,大臣由于各種原因不能來把要是交給國(guó)王钞钙。還有的時(shí)候,十個(gè)大臣都來了声离,但是誰也不知道該開哪個(gè)箱子芒炼,每次都要一個(gè)個(gè)是試也是很大無時(shí)間的。
***有 沒 有 辦 法 能 幫 助 國(guó) 王 來 管 理 他 這 些 箱 子 呢 ( ▔?з?▔ )抵恋?****

這個(gè) 精彩絕倫(漏洞百出)的故事講完了焕议,但是這里面其實(shí)引出來的問題值得我們探討一下的。方法太多了,我來簡(jiǎn)單說說幾個(gè)拙見吧盅安。

  • 方法一:
    將十個(gè)寶箱和每個(gè)大臣手里面的鑰匙按照對(duì)應(yīng)關(guān)系編號(hào)[0-9]唤锉,至于擺放順序其實(shí)無所謂了,每個(gè)箱子上都有自己的編號(hào)别瞭,每次大臣來齊了就能很快的打開所有的箱子了窿祥。(好像和之前比只是加上了序號(hào))

  • 總結(jié):
    1 能夠方便快速的打開箱子(真的快速嗎?蝙寨!難道箱子是亂放的晒衩,難道不需要找嗎)
    2 但是每次打開箱子都必須十個(gè)大臣到場(chǎng),如果少了其中某個(gè)就不能打開全部箱子了(而且每次去開箱子的時(shí)候都需要所有大臣在場(chǎng)墙歪,否則听系,去了五號(hào)箱子那,發(fā)現(xiàn)其余大臣都在虹菲,唯獨(dú)五號(hào)大臣不在靠胜,就抓瞎了)

  • 方法二:
    還是先將箱子和鑰匙進(jìn)行編號(hào),然后將9號(hào)箱子的鑰匙以及他所在位置放在8號(hào)箱子中毕源,將8號(hào)箱子的鑰匙以及他所在位置放在7號(hào)箱子中......將1號(hào)箱子的鑰匙以及他所在位置放在0號(hào)箱子中浪漠。
    完成了鑰匙和位置的分配,現(xiàn)在只需要一個(gè)大臣拿著鑰匙就能開啟所有的箱子啦~ ~ ~ ~ ~ ~

    • 總結(jié):
      1 只需保存一個(gè)箱子的鑰匙就可以管理所有的10個(gè)箱子啦(聽起來不錯(cuò)霎褐,但真就這么簡(jiǎn)單嗎址愿?!)
      2 不需要擔(dān)心鑰匙和箱子位置具體在哪的問題(真的嗎冻璃?O煳健)
      3 拿著鑰匙的那個(gè)大臣權(quán)利集中,一旦他丟了鑰匙俱饿,所有箱子都無法打開
      4 如果中間某個(gè)箱子的位置或者鑰匙放錯(cuò)了或者忘記了放進(jìn)去~~~~那么后續(xù)的箱子就等于丟了(拿到了第一把鑰匙也打不開后面的鎖了歌粥、、拍埠、失驶、)
      5 假設(shè)某一天國(guó)王想看看第十個(gè)箱子里面的東西的時(shí)候~ ~ ~那就麻煩了,得把前面幾個(gè)箱子都打開才行(十個(gè)箱子好像好像還好枣购,那假設(shè)有十萬個(gè)或者十億個(gè)箱子呢嬉探?他也許就只為了看看最后一個(gè)箱子就變成了一個(gè)開了一輩子箱子的國(guó)王(為啥突然想起來做木匠活的皇帝....))
      6 如果某天國(guó)王想要在這群箱子中插入個(gè)箱子,只需要管理需要插入位置前后的箱子就行了(這個(gè)優(yōu)點(diǎn)今天可能用不到(  ̄ー ̄)(  ̄ー ̄)就先放著吧)
  • 方法三:
    將十個(gè)箱子并排放在一起棉圈,[ 0 ][ 1 ][ 2 ][ 3 ]....[ 9 ]涩堤,箱子之間會(huì)形成自然順序。將下個(gè)箱子的鑰匙放在上個(gè)箱子上(這時(shí)候不需要保存位置啦分瘾,因?yàn)榫驮谒赃吢铮┨ノАR簿褪菍?號(hào)箱子的鑰匙以及他所在位置放在8號(hào)箱子上,將8號(hào)箱子的鑰匙以及他所在位置放在7號(hào)箱子上......將1號(hào)箱子的鑰匙以及他所在位置放在0號(hào)箱子上(注意沒有放在箱子里面!0谆辍汽纤!)。

    • 總結(jié)
      1 箱子的位置不用擔(dān)心了福荸,都在這一排擺著呢(但是需要考慮的是萬一蕴坪、、敬锐、沒有地方放下這么多連續(xù)的箱子可怎么辦背传,也就是對(duì)空間的要求更高了,十個(gè)箱子好像還好~~但是加入十萬個(gè)台夺、十億個(gè)箱子呢径玖?)
      2 保存一個(gè)鑰匙就可以管理全部的箱子了,也存在權(quán)利過分集中的問題谒养,如果挺狰,外面的那把鑰匙丟了、买窟、、薯定、箱子全都報(bào)廢了
      3 如果這時(shí)候國(guó)王想要看看其中某個(gè)箱子始绍,那么只需要按照序號(hào)去看看就行了,畢竟鑰匙就放在之前的箱子上面(如果這里是十萬個(gè)箱子话侄,國(guó)王也可以不費(fèi)勁的從里面找出他想要的那個(gè)~ ~ ~ ~)
      4 突然某一天國(guó)王想插入一個(gè)箱子亏推,如果箱子是插在最后沒啥問題,很容易年堆,但是吞杭、、变丧、芽狗、如果國(guó)王想要插入的位置是第一個(gè),那么國(guó)王得把所有的箱子往后諾一個(gè)位置才行痒蓬,如果是十億個(gè)箱子呢童擎?又出現(xiàn)了個(gè)挪了一輩子箱子的國(guó)王
  • 你TM弄了這么久箱子!攻晒!(# ?Д?)到底想干嘛(# ?Д?)顾复?!

其實(shí)這個(gè)故事雖然存在很多不合理的地方鲁捏,但實(shí)際是想做一個(gè)關(guān)于*** 數(shù) 據(jù) 結(jié) 構(gòu)*** 的比喻芯砸。

  • 每個(gè)箱子里面保存的“寶藏”,就相當(dāng)于——數(shù)據(jù)
  • 箱子以及箱子所處的位置,就相當(dāng)于——內(nèi)存

C/C++畢竟是*** 直 接 面 對(duì) 內(nèi) 存 ***的語言假丧,所以程序員難免要考慮這些“箱子們和鑰匙們” 的擺放和打開的問題###

***還 有 就 是 末购,這 些 管 理 方 法 后 面 肯 定 是 有 用 的 ~ ~ ~ ~ ~ ~, 就 先 放 在 這 里 虎谢, 我 之 后 再 提 ***


回到之前的一個(gè)問題盟榴,為什么類要用帶有指針和不帶指針來進(jìn)行區(qū)分呢?婴噩?擎场?

那么假設(shè)你是國(guó)王,你希望自己的箱子被打不開或者被找不到嗎几莽?
對(duì)于帶指針和不帶指針的class最大的區(qū)別就是是否需要管理這些放在箱子里面的“寶藏”Q赴臁!章蚣!如果管理不好站欺,那么會(huì)出現(xiàn)丟失找不到的情況,但是寶藏其實(shí)就在那里只是我們?cè)僖舱也坏搅讼舜梗@時(shí)候矾策,被丟失的寶藏就會(huì)出現(xiàn)一個(gè)問題,它會(huì)占用那些被我們找不到的空間峭沦。就像一個(gè)箱子的鑰匙丟了贾虽,箱子里面全是好東西,但是我們拿不到吼鱼,也砸不開那個(gè)箱子蓬豁,但是他還在你家里面占了很多空間。菇肃。地粪。。所以對(duì)于持有指針的class來說琐谤,手上拿到的指針蟆技,其實(shí)就相當(dāng)于那把鑰匙。管理鑰匙的本質(zhì)是為了管理箱子里的東西

class中帶不帶指針的問題關(guān)系著背后的數(shù)據(jù)(寶藏)該如何管理的問題

今天會(huì)用簡(jiǎn)化版本的string.h作為例子來說明帶有指針的class應(yīng)該如何設(shè)計(jì)笑跛,其實(shí)string相當(dāng)于之前的方法三的管理方法付魔,將所有的字符(char)在內(nèi)存中有一排連續(xù)的空間(數(shù)組),將每個(gè)字母放在連續(xù)的空間中飞蹂。就相當(dāng)于國(guó)王的十個(gè)箱子里面分別一個(gè)字母組成[ h ][ e ][ l ][ l ][ o ][ w ][ o ][ r ][ l ][ d ]的一串字符串几苍。這些“箱子”(內(nèi)存單元)是連續(xù)的排列的方式組合在一起。這時(shí)候陈哑,我們只需要拿著[ h ]這個(gè)箱子的鑰匙妻坝,就可以打開后面全部的箱子了伸眶,同時(shí)也可以根據(jù)每個(gè)箱子的位置來打開其中的箱子,取出其中的數(shù)據(jù)了(比如取出hello的[ o ]刽宪,他位于這一排箱子的第五個(gè)位置厘贼,如果從0號(hào)開始排序,那么他就是相當(dāng)于這串箱子的4號(hào)了圣拄,我們只需要拿著這個(gè)4號(hào)就能去走這個(gè)o了)嘴秸。

后面該適當(dāng)嚴(yán)肅點(diǎn)了,但還會(huì)呼應(yīng)之前的故事庇谆,來解釋 我想解釋的內(nèi)容 (′?ω?`) )

帶有指針的class需要解決的Big Three問題

class String
{
    public:     
    //構(gòu)造函數(shù)岳掐,接收一個(gè)字符串?dāng)?shù)組,通過字符串?dāng)?shù)組生成一個(gè)字符串(cstr相當(dāng)于保管起來的那個(gè)鑰匙)                              
       String(const char* cstr=0);
    //構(gòu)造函數(shù)(拷貝構(gòu)造饭耳,對(duì)于含有指針的class尤其要注意的)
    //接收參數(shù)為一個(gè)String本身串述,也就是通過接收String本身來構(gòu)造一個(gè)新的String對(duì)象
       String(const String& str);       
    //操作符重載,(拷貝賦值寞肖,對(duì)于含有指針的class尤其要注意的)
    //操作符左右兩側(cè)均為String對(duì)象纲酗,也就是將一個(gè)String對(duì)象賦值給另外一個(gè)已有的String對(duì)象
       String& operator=(const String& str);
    //析構(gòu)函數(shù),對(duì)象死亡之前會(huì)默認(rèn)調(diào)用的函數(shù)
    //防止指針失效了新蟆,但實(shí)際的數(shù)據(jù)還存在在內(nèi)存中觅赊,無法拿到,也無法改變和刪除
    //會(huì)造成內(nèi)存泄漏栅葡,在對(duì)象死亡前需要再次管理那塊內(nèi)存空間
       ~String();                
    //獲取私有屬性茉兰,獲取時(shí)沒有改變內(nèi)容,需要注意const的修飾(找鑰匙的方法)                    
       char* get_c_str() const { return m_data; }
    private:
       char* m_data;  //相當(dāng)于管理這一串寶箱的第一把鑰匙
};
  • 析構(gòu)函數(shù)

    • 析構(gòu)函數(shù)是在對(duì)象生命周期的最末尾也就是對(duì)象即將被銷毀的時(shí)候進(jìn)行調(diào)用的函數(shù)欣簇,主要是為了方便清楚帶有指針變量的對(duì)象,能夠在對(duì)象清楚之前坯约,清理其指針?biāo)赶虻哪遣糠謨?nèi)存空間熊咽,以避免造成必要的內(nèi)存泄漏。

      inline
      String::~String()
      {
       //刪除字符串的內(nèi)存空間中保存的內(nèi)容闹丐,釋放內(nèi)存
       //字符串本質(zhì)上為字符數(shù)組横殴,所以再刪除的時(shí)候,需要使用delete[]
       delete[] m_data;
      

      }

  • 復(fù)制拷貝問題

    • 與不帶指針的對(duì)比
      • 對(duì)于不帶指針的class
        • 編譯器會(huì)自動(dòng)幫我們實(shí)現(xiàn)拷貝構(gòu)造和拷貝復(fù)制兩種方法
        • 并且編譯器會(huì)依照內(nèi)存中所保存的數(shù)據(jù)卿拴,依照每個(gè)byte進(jìn)行拷貝衫仑,相當(dāng)于兩份完全相同的數(shù)據(jù)
        • 如果能夠滿足需求不需要重寫這兩種方法
      • 對(duì)于帶有指針的class
        • 編譯器會(huì)默認(rèn)拷貝指針變量(配一把鑰匙),但實(shí)際指針?biāo)赶虻臄?shù)據(jù)并未被復(fù)制一份堕花,導(dǎo)致兩個(gè)指針同時(shí)指向一個(gè)內(nèi)存空間(兩把一樣的鑰匙開的同一個(gè)箱子文狱,但是實(shí)際箱子里面的內(nèi)容依然只有一份,不要假裝騙自己說配了兩把鑰匙就擁有了兩份數(shù)據(jù)的道理相同)
        • 如果使用了賦值符(A = B)的時(shí)候缘挽,相當(dāng)于將鑰匙A的鑰匙改造成鑰匙B瞄崇,但是對(duì)于A原始可以管理的那個(gè)箱子就再也沒辦法打開了呻粹,而A和B可以同時(shí)管理同一個(gè)箱子,沒辦法實(shí)現(xiàn)通過賦值符來實(shí)現(xiàn)賦值一個(gè)和B相同的箱子苏研。為了避免這個(gè)問題等浊,實(shí)現(xiàn)deep copy,必須重寫賦值符左右相同都是自己本身類型的操作符
        • class中持有指針變量摹蘑,那么對(duì)于賦值過程需要考慮重寫拷貝構(gòu)造和拷貝賦值
    • 拷貝構(gòu)造(Copy Constructor)
      • 通過傳入和自己本身類型相同的對(duì)象筹燕,來構(gòu)造一個(gè)新的自己類型的對(duì)象

        String s1("hello");
        String s(s1);    //第一種寫法,構(gòu)造一個(gè)新的String對(duì)象s衅鹿,初值為s1中的內(nèi)容
        String s = s1;  //第二種寫法撒踪,構(gòu)造一個(gè)新的String對(duì)象s,初值為s1中的內(nèi)容
        
      • 拷貝過后會(huì)生成一個(gè)新的對(duì)象(s)

    inline
    String::String(const String& str)  //參數(shù)就是自己類型本身塘安,可以使用引用糠涛,因?yàn)閭魅氲臄?shù)據(jù)不會(huì)被改動(dòng),需要使用const修飾
    {
        //因?yàn)槭切聞?chuàng)建的String對(duì)象兼犯,所以直接申請(qǐng)足夠的內(nèi)存空間忍捡,將內(nèi)容拷貝過去即可
       m_data = new char[ strlen(str.m_data) + 1 ];
       strcpy(m_data, str.m_data);    //str.m_data:相同類型的對(duì)象互為友元,可以直接讀取內(nèi)部的private屬性
    }
  • 拷貝賦值(Copy Assignment Operator)
    • 將一個(gè)對(duì)象切黔,復(fù)制給另外一個(gè)** 已 有 **的對(duì)象

    • 思路:先將原對(duì)象中的內(nèi)容清空砸脊,再將內(nèi)容進(jìn)行復(fù)制

      {
         String s1("hello");
         String s2("hello");
         s2 = s1; //將s1的內(nèi)容復(fù)制給s2,s2在復(fù)制之前實(shí)際是一個(gè)已存在的對(duì)象
      }
      
    • 函數(shù)定義

      inline
      

      String& String::operator=(const String& str)
      {
      //自我賦值檢查
      //作用:
      //1. 如果將s賦值給s纬霞,則無需操作凌埂,那么可以直接通過return來跳出該函數(shù),減少多余操作诗芜,提高效率
      //2. 如果此處不做判斷瞳抓,直接執(zhí)行后續(xù)代碼,會(huì)在復(fù)制之前先釋放等待接受內(nèi)容的指針?biāo)赶虻目臻g伏恐,
      // 因?yàn)椴僮鞣笥蚁嗤⒀疲瑫?huì)造成將被復(fù)制的對(duì)象那么部分給釋放,最終破壞原始的數(shù)據(jù)
      if (this == &str)
      return *this;
      delete[] m_data; //清空原有對(duì)象的內(nèi)存翠桦,防止造成內(nèi)存泄漏
      m_data = new char[ strlen(str.m_data) + 1 ]; //在堆上重新申請(qǐng)對(duì)應(yīng)長(zhǎng)度的空間
      strcpy(m_data, str.m_data); //復(fù)制內(nèi)容到新申請(qǐng)的空間
      return *this; //返回賦值符左側(cè)的指針變量所指向的內(nèi)容
      }


string.h的完整代碼

    #ifndef __MYSTRING__
    #define __MYSTRING__

    class String
    {
    public:                                 
       String(const char* cstr=0);                     
       String(const String& str);                    
       String& operator=(const String& str);         
       ~String();                                    
       char* get_c_str() const { return m_data; }
    private:
       char* m_data;
    };

    #include <cstring>
    //通過字符串?dāng)?shù)組來構(gòu)造一個(gè)字符串對(duì)象的構(gòu)造函數(shù)
    inline
    String::String(const char* cstr)
    {
       //需要對(duì)傳入的參數(shù)進(jìn)行檢查是否有內(nèi)容
       if (cstr) {
          //需要針對(duì)傳入的字符串?dāng)?shù)組的長(zhǎng)度(strlen(cstr)來獲得),但字符串后需要放置一個(gè)結(jié)束符/0横蜒,所以長(zhǎng)度需要+1
          m_data = new char[strlen(cstr)+1];//在堆內(nèi)存上,申請(qǐng)一塊長(zhǎng)度比傳入字符數(shù)組長(zhǎng)1的字符數(shù)組的內(nèi)存空間
          strcpy(m_data, cstr);//使用strcpy函數(shù)將傳入的參數(shù)cstr的所指的內(nèi)存中的數(shù)據(jù)销凑,賦值到新申請(qǐng)的內(nèi)存空間上去丛晌,使兩塊內(nèi)存空間都同時(shí)具備相同的數(shù)據(jù)
       }
       else {   //未指定初值的情況
       //雖然傳入的部分為空,但是由于在C/C++中斗幼,管理字符串是以\0作為結(jié)尾的澎蛛,即使是空字符串實(shí)際并不完全為空
       //所以在需要構(gòu)造長(zhǎng)度為一個(gè)單位的字符串?dāng)?shù)組,并在其中放入結(jié)束符/0
          m_data = new char[1];  //申請(qǐng)空間
          *m_data = '\0';       //保存結(jié)束符
       }
    }

    inline
    String::~String()
    {
       delete[] m_data;
    }

    inline
    String& String::operator=(const String& 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;
    }

    inline
    String::String(const String& str)
    {
       m_data = new char[ strlen(str.m_data) + 1 ];
       strcpy(m_data, str.m_data);
    }
      //輸出孟岛,<<符號(hào)重載瓶竭,不能為成員函數(shù)督勺,因?yàn)椴僮鞣髠?cè)為ostream
    #include <iostream>
    using namespace std;
        ostream& operator<<(ostream& os, const String& str)
    {
       os << str.get_c_str();
       return os;
    }
    #endif

內(nèi)存管理初探

  • 堆(heap)和棧(stack)

      • 棧,是存在于某作用域(scope)的一塊內(nèi)存空間斤贰。例如智哀,當(dāng)你調(diào)用的函數(shù),函數(shù)本身就會(huì)形成一個(gè)棧荧恍,用來存放所接收的參數(shù)瓷叫,以及返回地址。在函數(shù)本體內(nèi)聲明的任何變量送巡,其所使用的內(nèi)存塊都在棧上摹菠。
      • 棧,其實(shí)是一種特殊的數(shù)據(jù)結(jié)構(gòu)骗爆,他的特性是先入后出(后入先出)次氨,就像一摞放在桌子上面的碟子,我們清洗好是依次往上摞在一起的摘投,而最先洗好的盤子位于最底下煮寡,我們?nèi)”P子的時(shí)候一般都是先從頂部直接拿就行了,所以造成了后洗好的盤子最先被取出來犀呼,也就是先入棧的幸撕,后出棧。對(duì)于函數(shù)來說無論接收的參數(shù)還是創(chuàng)建的參數(shù)外臂,都是最先得到的最后才出棧(被釋放坐儿,也就是對(duì)象死掉),后得到或創(chuàng)建的先出棧宋光。
      • 特點(diǎn):
        1 對(duì)于操作系統(tǒng)來說貌矿,程序員不需要管理?xiàng)I系淖兞浚拷唤o系統(tǒng)調(diào)度和處理罪佳,效率高
        2 使用簡(jiǎn)單遂黍,但空間較小池摧,每個(gè)程序、函數(shù)都有自己的棧持钉,相對(duì)安全性高
      • 堆投储,是指有操作系統(tǒng)所提供的一塊全局的內(nèi)存空間第练,程序可以動(dòng)態(tài)分配(dynamic allocated)從中獲得若干區(qū)塊(blocks)。
      • 特點(diǎn)
        1 系統(tǒng)共用區(qū)域玛荞,不同的程序都可以訪問同一塊區(qū)域娇掏,不釋放內(nèi)存空間會(huì)造成內(nèi)存泄漏
        2 需要自己手動(dòng)來申請(qǐng),同時(shí)也需要手動(dòng)來釋放勋眯,如果不釋放婴梧,當(dāng)處在棧中的俄變量被彈出下梢,則這部分空間丟失,會(huì)造成內(nèi)存泄漏(鑰匙丟了找不到箱子的情況)
  • 對(duì)象的創(chuàng)建(TypeName代表類型名稱塞蹭,比如自定義類型String等等孽江,params...代表傳入的參數(shù))

    • 方式一:TypeName t(params...);
      • 所占用的空間來自于棧
    • 方式二:TypeName *pt = new TypeName(params...);
      • 實(shí)際該語句所需要的空間來自棧和堆兩部分,其中番电,實(shí)際存儲(chǔ)的數(shù)據(jù)的內(nèi)存空間來自于堆岗屏,而變量pt的保存空間實(shí)際是在棧的,而存在棧里面pt由于是一個(gè)指針漱办,它記錄了所申請(qǐng)的堆中空間的地址(也就是箱子的位置)这刷。由于pt的存儲(chǔ)空間為棧,所以當(dāng)t出了所在的作用域之后娩井,t會(huì)被棧彈出暇屋,而堆上的內(nèi)容并不會(huì)被釋放,并且永遠(yuǎn)再?zèng)]有機(jī)會(huì)被delete了洞辣。如果在彈出前咐刨,未釋放堆上的那部分空間,則就造成了內(nèi)存泄漏了(其實(shí)內(nèi)存泄漏就相當(dāng)丟了鑰匙的寶箱屋彪,知道他的存在所宰,但是再也沒辦法拿到了,占用了不必要的系統(tǒng)資源畜挥,寶箱的花就相當(dāng)于丟了自己的錢W兄唷!但明知這部分錢是自己的蟹但,但是你就是沒辦法找到和證明了G!;恰)麦向。
  • 靜態(tài)對(duì)象(static object):這個(gè)對(duì)象其實(shí)就是一個(gè)"超級(jí)宅男 "。一直被保存在內(nèi)存中的靜態(tài)區(qū)(用于保存全局(global)和靜態(tài)(static)變量的內(nèi)存區(qū)域)客叉,并且被static所修飾的對(duì)象诵竭,聲明將會(huì)超過整個(gè)作用域(scope),畢竟他不在棧中兼搏。他的生命周期會(huì)貫穿程序的全生命周期卵慰。

  • 全局對(duì)象(global object):定義在***任何 *** 作用域({ ... })之外的變量。他和靜態(tài)對(duì)象一樣保存在內(nèi)存的靜態(tài)區(qū)部分佛呻,他的生命周期也是在整個(gè)程序結(jié)束后才會(huì)結(jié)束裳朋。

  • new關(guān)鍵字:先分配memory,再調(diào)用Constructor

    • TypeName *pt = new TypeName(params...);的編譯器處理過程
      1 void *mem = operator new (sizeof(TypeName)); //分配所需的內(nèi)存吓著,operator new的內(nèi)部調(diào)用了malloc(n)來分配空間
      2 pt = static_cast<TypeName *>(mem); //類型強(qiáng)轉(zhuǎn)
      3 pt->TypeName::TypeName(params...); //調(diào)用class所對(duì)應(yīng)的構(gòu)造函數(shù)
  • delete關(guān)鍵字:先分配disconstructor鲤嫡,再釋放內(nèi)存

    • delete pt;的編譯器處理過程
      1 TypeName::~TypeName(pt); //調(diào)用析構(gòu)函數(shù)
      2 operator delete(pt); //釋放內(nèi)存送挑,operator delete內(nèi)部調(diào)用了free(pt)
  • 動(dòng)態(tài)分配內(nèi)存的結(jié)構(gòu):所分配空間的大小都是16byte的倍數(shù)

    • debug模式
      • cookie (4byte)
      • debuger header(4*8byte=32byte)
      • 對(duì)象所需的空間(n byte)
      • no man land (4byte)
      • pad區(qū)域(填補(bǔ)區(qū)域,當(dāng)總內(nèi)存不為16byte倍數(shù)時(shí)暖眼,不足為最近16byte倍數(shù)所占用的空間)
      • cookie(4byte)
    • release模式
      • cookie (4byte)
      • 對(duì)象所需的空間(n byte)
      • pad區(qū)域(填補(bǔ)區(qū)域惕耕,當(dāng)總內(nèi)存不為16byte倍數(shù)時(shí),不足為最近16byte倍數(shù)所占用的空間)
      • cookie(4byte)
    • cookie部分保存參數(shù)的意義
      • 例如 cookie中保存為0x00000041:64byte的十六進(jìn)制為0x40罢荡,則0x00000040表示這部?jī)?nèi)存空間的大小赡突,而最低位1表示程序獲得內(nèi)存(系統(tǒng)給出的內(nèi)存),以此來作為標(biāo)識(shí)告訴系統(tǒng)這塊空間的大小和狀態(tài)区赵,同樣cookie也就變成了0x00000041惭缰。為了方便cookie能夠使用十六進(jìn)制最低位表示狀態(tài),高位表示控件大小笼才,則內(nèi)存控件需要時(shí)16byte的的倍數(shù)漱受,因?yàn)橹挥?6byte的倍數(shù)十六進(jìn)制數(shù)打的最低位才為0,也就是16(DEC) = 0x10(HEX)
    • 對(duì)于數(shù)組的動(dòng)態(tài)分配的內(nèi)存結(jié)構(gòu)
      • debug模式
        • cookie (4byte)
        • debuger header(4*8byte=32byte)
        • 記錄保存的對(duì)象個(gè)數(shù)(4byte)
        • 對(duì)象1所需的空間(n byte)
        • 對(duì)象2所需的空間(n byte)
        • .....
        • 對(duì)象n所需的空間(n byte)
        • no man land (4byte)
        • pad區(qū)域(填補(bǔ)區(qū)域骡送,當(dāng)總內(nèi)存不為16byte倍數(shù)時(shí)昂羡,不足為最近16byte倍數(shù)所占用的空間)
        • cookie(4byte)
      • release模式
        • cookie (4byte)
        • 記錄保存的對(duì)象個(gè)數(shù)(4byte)
        • 對(duì)象1所需的空間(n byte)
        • 對(duì)象2所需的空間(n byte)
        • .....
        • 對(duì)象n所需的空間(n byte)
        • pad區(qū)域(填補(bǔ)區(qū)域,當(dāng)總內(nèi)存不為16byte倍數(shù)時(shí)摔踱,不足為最近16byte倍數(shù)所占用的空間)
        • cookie(4byte)
    • 由于分配的內(nèi)存的結(jié)構(gòu)虐先,所以導(dǎo)致new [],要搭配對(duì)應(yīng)delete[]來釋放空間,否則會(huì)造成內(nèi)存泄漏派敷,對(duì)應(yīng)的delete[]會(huì)調(diào)用析構(gòu)函數(shù)多次蛹批,可以將需要釋放的內(nèi)存都釋放掉

補(bǔ)充部分:

  • static關(guān)鍵字
    • static 和non-static
      • 非靜態(tài)對(duì)象:在內(nèi)存中會(huì)創(chuàng)建多份

      • 靜態(tài)對(duì)象:僅在靜態(tài)區(qū)創(chuàng)建一份

      • 非靜態(tài)成員函數(shù)

        complex c1;
        cout << c1.real();  //調(diào)用complex中成員函數(shù)real()返回對(duì)應(yīng)的實(shí)部
        //實(shí)際相當(dāng)于cout << complex::real(&c1);
        //調(diào)用該函數(shù)的對(duì)象c1的地址相當(dāng)于成員函數(shù)中的this,傳入到該函數(shù)中
        
      • 靜態(tài)的函數(shù)(無this)

        • 由于靜態(tài)的函數(shù)中沒有this篮愉,所以只能被處理靜態(tài)的屬性
class Account{
public:
  static double m_rate;
  static void set_rate(const dounle& x){m_rate = x;};
};
double Account::m_rate = 8.0;  //對(duì)于靜態(tài)數(shù)據(jù)必須在函數(shù)外進(jìn)行定義腐芍,設(shè)置不設(shè)置初始值都可以

int main() 
{
  Account::set_rate(5.0);  //通過class name調(diào)用
  Account a;
  a.set_rate(7.0);  //通過object調(diào)用,對(duì)象的地址不會(huì)將a的地址作為this傳入函數(shù)中
}
  • Singleto的設(shè)計(jì)模式(將構(gòu)造函數(shù)放在private中)

    • 懶漢模式:先創(chuàng)建好對(duì)象试躏,需要時(shí)直接返回該對(duì)象即可

      class A
      {
       public:
        static A& getInstance(){ return a; }
        setup(){....}
       private:
          A();
          A(const A& rhs);
          static A a;
          ...
      }
      
    • 餓漢模式(Meyers Singleton):在調(diào)用的時(shí)候才創(chuàng)建所需要的對(duì)象猪勇,由于是靜態(tài),則內(nèi)存中式中只有一份所需要的對(duì)象

      class A
      {
       public:
        static A& getInstance(){ return a; }
        setup(){....}
       private:
          A();
          A(const A& rhs);
          ...
      };
      A& A::getInstance()
      {
        static A a;
        return a;
      }
      
  • class template颠蕴,類模版

    template <typename T>
    class complex
    {
      public:
        complex(T r = 0, T i = 0)
        : re(r), im(i){  }
        complex &operator +=(const complex&);
        T real() const{ return re; }
        T imag() const { return im; }
    private:
        T re, im;
        friend complex& __doapl(complex *, const complex *);
    };
    

    {
    complex<double> c1(2.5, 1.5);
    complex<int> c2 (2, 6);
    }

  • function template泣刹,函數(shù)模版

    class stone
    {
     public:
        stone(int w, int h, int we): _w(w), _h(h), _weight(we){}
        bool operator< (const stone & rhs) const {return _weight < rhs._weight; }
    private:
        int _w, _h, _weight;
    };
    template <class T>
    inline
    const T& min(const T& a, const T& b)
    {
       return b<a? b: a; 
    }
    .....
    {
      stone r1(2, 3), r2(3, 3), r3; 
      //無需明確指出,編譯器對(duì)function template進(jìn)行參數(shù)推到(argument deduction)
      //參數(shù)推倒結(jié)果犀被,T為stone项玛,于是替換所有的T為stone,調(diào)用stone::operator<
      r3 = min(r1, r2);
    }
    
  • namespace:相當(dāng)于一塊封閉的空間

     namespace std
     {
       ....
     }
    
    • using directive:相當(dāng)于打開全部的封鎖

      #include <iostream.h>
      using namespace std;
      int main()
      {
          cin << ...;
          return 0;
      }
      
    • using declaration:只是用某一條的聲明

      #include <iostream.h>
      using namespace std::cout;
      int main()
      {
          std::cin << ...;
          cout<<...;
          return 0;
      }
      
  • 不使用using namespace

      #include <iostream.h>
      using namespace std::cout;
      int main()
      {
          std::cin << ...;
          std::cout<<...;
          return 0;
      }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末弱判,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子锥惋,更是在濱河造成了極大的恐慌昌腰,老刑警劉巖开伏,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異遭商,居然都是意外死亡固灵,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門劫流,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巫玻,“玉大人,你說我怎么就攤上這事祠汇∪猿樱” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵可很,是天一觀的道長(zhǎng)诗力。 經(jīng)常有香客問我,道長(zhǎng)我抠,這世上最難降的妖魔是什么苇本? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮菜拓,結(jié)果婚禮上瓣窄,老公的妹妹穿的比我還像新娘。我一直安慰自己纳鼎,他們只是感情好俺夕,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著喷橙,像睡著了一般啥么。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贰逾,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天悬荣,我揣著相機(jī)與錄音,去河邊找鬼疙剑。 笑死氯迂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的言缤。 我是一名探鬼主播嚼蚀,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼管挟!你這毒婦竟也來了轿曙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎导帝,沒想到半個(gè)月后守谓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡您单,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年斋荞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虐秦。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡平酿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出悦陋,到底是詐尸還是另有隱情蜈彼,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布叨恨,位于F島的核電站柳刮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏痒钝。R本人自食惡果不足惜秉颗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望送矩。 院中可真熱鬧蚕甥,春花似錦、人聲如沸栋荸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)晌块。三九已至爱沟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匆背,已是汗流浹背呼伸。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钝尸,地道東北人括享。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像珍促,于是被迫代替她去往敵國(guó)和親铃辖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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