C++自制心得——類與對象上
前言:?
從這里開始,我們將面對C++的第一個攔路虎,類與對象。這里的內(nèi)容大概要寫四個專欄,開篇一章,六大成員函數(shù)兩章,收尾一章,每一章都是超級大長篇,請做好心里準備。而且,至少在類與對象這一節(jié),沒有幾個語言比C++更麻煩,一是C++需要向下兼容C語言,二是C++出的早,坑會多。廢話不多講,我們開始吧。
1.類與結(jié)構(gòu)體
我們這里不講面向?qū)ο蟮木唧w定義,別人都講爛了,自己找一個看就是。我們從結(jié)構(gòu)體開始講,在C++里,結(jié)構(gòu)體直接被升級成了類,同時也保有了C語言原本的用法。
s1是C語言用法,s2是C++的用法。升級后的結(jié)構(gòu)體支持成員函數(shù),并且定義對象時也不用加struct關(guān)鍵字了。
不過在實際應(yīng)用中我們更喜歡用類來代替結(jié)構(gòu)體,如圖
但是換成類以后程序就跑不了了,如圖

這是什么錯誤,你不是說結(jié)構(gòu)體被升級成了類嗎?怎么換了類就報錯了。其實類與結(jié)構(gòu)體還是有一點區(qū)別的,那就是默認訪問限定符的不同。
訪問限定符有三種類型,public(公有),private(私有),protected(保護),目前,我們可以認為私有和保護是一樣的(它們的區(qū)別在繼承和多態(tài)才有體現(xiàn),現(xiàn)在只講封裝)
被public修飾的成員可以在類外通過正常方式訪問,而被private修飾的成員不能在類外通過正常方式訪問。而結(jié)構(gòu)體的默認訪問限定符為公有,類的默認訪問限定符為私有,這就是報錯原因。
那么,類將private作為默認修飾符有什么好處嗎?上代碼
這里有一個棧,現(xiàn)在我們想訪問其棧頂元素,那就寫一個top函數(shù)訪問,一句話的事。不過,某些人喜歡用另一種方案,他們覺得訪問棧頂元素可以直接用top變量訪問,沒必要寫一個函數(shù),想法還不錯,但是......

為什么結(jié)果是隨機值,我相信觀眾們都清楚,不過這種編程思路就反映出C語言的一大痛點,過于自由。作為代碼的設(shè)計者,我們自然希望用戶只通過我們提供的接口使用代碼,而不希望用戶自由訪問。顯然用private修飾我們不想讓用戶訪問的成員是很科學的,這就是封裝的好處。
2.類的聲明與定義
這個問題很簡單,我就直接貼代碼了
stack.h
stack.cpp
前四個成員函數(shù)不用管,后面會講,我們看后面的。類的聲明定義規(guī)則實際上是函數(shù)聲明定義與結(jié)構(gòu)體聲明定義的集成,成員變量的聲明定義按照結(jié)構(gòu)體的來,成員函數(shù)的聲明定義按照函數(shù)的來,唯一需要注意的是成員函數(shù)的定義處在全局而非類域里,所以定義處需要添加::
3.成員變量風格
你應(yīng)該已經(jīng)發(fā)現(xiàn)了,我給出的示例代碼里成員變量在前面都有“_”符號,你可能在學習C語言的時候也被告知要給成員變量加修飾。這當然是有理由的,我以日期類的init來舉例子。
如果你不給成員變量加修飾,那這個init就起不到它應(yīng)有的功能,編譯器會將它認定為自己給自己賦值(你要愿意用this當我沒說)?
這就行了。
4.實例化
可以這么寫代碼嗎?
不行,因為這個類沒有實例化,我們訪問的只是一個類型

將類比作房子圖紙,實例化的對象比作房子,邏輯會很清晰,房子的圖紙不能住人,但房子可以。把圖紙變成房子的過程就是實例化,這個過程就會給對象開空間,你就能訪問了。
再看一個問題,對象d1占用多少空間?根據(jù)內(nèi)存對齊可以得出d1的成員變量所占空間為12字節(jié),但是成員函數(shù)占不占空間?不知道,那就試試。

哦,結(jié)果是12,看上去成員函數(shù)不占對象空間,事實也的確如此。實例化對象只會給成員變量開空間,而成員函數(shù)不會進入具體對象本身,而是會進入公共代碼區(qū)。這是符合邏輯的,成員變量需要有很多份,而函數(shù)只需要一份,顯然,把成員函數(shù)放入對象就是一件特別浪費的事情。
上點難度,這三個對象的空間是多少?







揭曉答案

第一二個很好理解,默認對齊數(shù)為8,所以第一個對齊數(shù)為4,第二個對齊數(shù)為8,所以占用空間為8和24,但是第三個為什么是1,不是0。是這樣的,如果第三個是0,我們就無法區(qū)分多個A3類的對象,因此我們象征性的給了一個字節(jié)以示區(qū)分。
5.this指針
你覺得init函數(shù)里的成員變量是誰的成員變量?







哪個對象調(diào)用了這個函數(shù),函數(shù)里的成員變量就是那個對象的。但是從表面上看init函數(shù)的形參只有年月日要被賦的值,沒有調(diào)用對象的年月日,那么編譯器是怎么做到這一點的?答案就是this指針。C++給每一個成員函數(shù)都添加了一個隱含參數(shù)this指針,固定為函數(shù)的第一形參,指向調(diào)用對象,不可更改指向(類型 + * const this),并且其調(diào)用和傳參均由編譯器自動進行,不可顯式寫出,但在函數(shù)里可顯式寫出。如果我們把this指針顯式寫出,那應(yīng)該是這樣的
( ̄▽ ̄)現(xiàn)在我要出三道題,看看各位對this指針的理解是否到位。
第一個代碼的運行結(jié)果是:A. 編譯錯誤? B. 運行錯誤 C. 正常運行
第二個代碼的運行結(jié)果是:A. 編譯錯誤? B. 運行錯誤 C. 正常運行
第三個代碼的運行結(jié)果是:A. 編譯錯誤? B. 運行錯誤 C. 正常運行







揭曉答案,CCB



以我在這一塊學習的經(jīng)驗來說一定有很多人選BBB。那么各位要想清楚一個問題,在a->print()這句代碼里,print()是不是a直接指向的?No,前面我們說了,成員函數(shù)不在對象里,在公共代碼區(qū),那實際上a直接指向的只有int x,顯然,在第一二種情況里,a壓根沒有解引用,代碼實際上只完成了nullptr的拷貝,而沒有解引用,所以代碼可以正常運行。只有第三種情況我們解引用了nullptr。為了驗證我的結(jié)論,以第一種情況為例,我們?nèi)シ磪R編。

看見了嗎,a->print()只干了兩件事,將nullptr賦給ecx寄存器(C++覺得this指針會被經(jīng)常調(diào)用,所以會把this指針存在寄存器里,而非進行壓棧操作),調(diào)用print函數(shù),沒有解引用操作。
下面再出兩道面試題:
1. this指針可以為空嗎?
2. this指針存在哪里?







第一題我們演示過了,可以為空。第二題的答案是棧區(qū),為什么,因為this是函數(shù)形參,函數(shù)形參存在棧區(qū)(有沒有人寫this指針存在對象里?有沒有?有沒有?)
可喜可賀,類與對象上終于講完了,下一個就是六大成員函數(shù),到時候,等著被虐吧。
C++自制心得——類與對象上的評論 (共 條)
