struct和class之间问题(+引发的个人思考,和对共用体思考)
一、C語言中的struct
在C語言中,struct是一種自定義數(shù)據(jù)類型,所以在C語言中,struct不能包含任何函數(shù),否則編譯器會(huì)報(bào)錯(cuò)。因?yàn)镃語言是面向過程的編程。采用過程性編程首先考慮要遵循的步驟,然后考慮如何表示這些數(shù)據(jù)。也就是先考慮過程后考慮數(shù)據(jù),這樣方法和數(shù)據(jù)就分開了。而在C語言中結(jié)構(gòu)體就是為了考慮數(shù)據(jù)的封裝而存在的。
還有就是在C語言中不能存在空結(jié)構(gòu)體,否則編譯器會(huì)報(bào)錯(cuò)。原因很簡(jiǎn)單,C語言中結(jié)構(gòu)體就是為了封裝數(shù)據(jù)類型,統(tǒng)一為一個(gè)新的struct數(shù)據(jù)類型,既然是封裝數(shù)據(jù)而存在,那么定義一個(gè)結(jié)構(gòu)體不給任何數(shù)據(jù),那還封裝個(gè)什么。
所以總結(jié)就是:
(1)C語言中的結(jié)構(gòu)體只是封裝數(shù)據(jù)用的,不能包含任何函數(shù)。
(2)C語言中的結(jié)構(gòu)體不能為空結(jié)構(gòu)體。
二、C++中的struct
首先C++是面向?qū)ο蟮木幊獭J紫瓤紤]數(shù)據(jù),同時(shí)還要考慮如何使用數(shù)據(jù)。對(duì)待一種事物,首先用一個(gè)對(duì)象表示這個(gè)事物 的方方面面,包括對(duì)象的屬性,和對(duì)對(duì)象的屬性(數(shù)據(jù))的操作。即首先從用戶的角度考慮對(duì)象,描述對(duì)象所需要的數(shù)據(jù),以及描述用戶與數(shù)據(jù)交互所需要的操作。完成對(duì)接口的描述后,需要確定如何實(shí)現(xiàn)接口和數(shù)據(jù)的存儲(chǔ),最后使用新的設(shè)計(jì)方案創(chuàng)建出程序。這里可以說就是面向?qū)ο蟮脑瓌t之一:“依賴倒轉(zhuǎn)原則”。依賴接口編程序。
在C++中struct與class沒有什么特別的區(qū)別。唯有暢談的一個(gè)區(qū)別,就是struct里的數(shù)據(jù)成員默認(rèn)都是public類型的,而class里面的數(shù)據(jù)類型默認(rèn)是private類型的。為什么會(huì)這樣呢?主要還是為了兼容C語言程序中的結(jié)構(gòu)體,C語言程序中的結(jié)構(gòu)體就是隨便訪問數(shù)據(jù)的。C++里在兼容C語言程序中的struct的同時(shí),盡量將struct和class的其他屬性設(shè)置成一致。所以C++中的struct除了默認(rèn)屬性是public之外,其他和class的使用沒有區(qū)別,包括構(gòu)造函數(shù),析構(gòu)函數(shù),可以有空類,也允許有空結(jié)構(gòu)體。
所以總結(jié)就是:
(1)C++中的結(jié)構(gòu)體默認(rèn)屬性是public屬性。
(2)C++中的結(jié)構(gòu)體出去第一點(diǎn)之外,其他的使用和class一樣。包括有默認(rèn)構(gòu)造函數(shù),默認(rèn)析構(gòu)函數(shù),可以自定義構(gòu)造函數(shù),可以有其他成員函數(shù),也可以像class一樣為一個(gè)空體。
另外在C語言里結(jié)構(gòu)體內(nèi)的變量不可以初始化,因?yàn)樵谟媒Y(jié)構(gòu)體定義變量之前,沒有給struct分配棧空間(在網(wǎng)上搜了好多,都是這樣說的),換句話說C語言里struct只是一個(gè)自定義變量類型,怎么可能直接給變量類型里面的東西賦值呢?比如int也是一個(gè)變量類型,怎么可能把int里面的東西賦值呢,所以struct也是這個(gè)意思。只有用struct定義出來變量的時(shí)候,給定義出來的變量賦值。
注意,這個(gè)時(shí)候就更不能將C++的結(jié)構(gòu)體和C語言的結(jié)構(gòu)體相提并論,因?yàn)镃語言結(jié)構(gòu)體只是一個(gè)單純的自定義數(shù)據(jù)類型,但是在這里struct已經(jīng)超脫普通自定義類型概念了,因?yàn)檫@里struct里面可以用成員函數(shù),是封裝數(shù)據(jù)和方法的類型,是一個(gè)包含代碼實(shí)現(xiàn)設(shè)計(jì)的封裝。
但是C++的結(jié)構(gòu)體和類卻可以初始化,按道理說,類和結(jié)構(gòu)體我們都說沒有實(shí)例化,沒有分配棧空間。創(chuàng)建對(duì)象才是實(shí)例化,分配棧空間,構(gòu)建對(duì)象,這個(gè)時(shí)候才有真正意義。這樣想的話,感覺C++里面的類和結(jié)構(gòu)體也不可以給成員變量初始化。但是事實(shí)是可以初始化的。
如下變量初始化代碼及運(yùn)行情況:
如果我們不初始化直接輸出呢?我沒有敲代碼看結(jié)果之前,我覺得要么就是隨機(jī)值,要么就是0。我在博客上還搜了一下,發(fā)現(xiàn)有一篇**2015年博客**說在C++里結(jié)構(gòu)體里面變量不初始化的話,創(chuàng)建對(duì)象輸出變量值是0,類創(chuàng)建對(duì)象,輸出成員變量值是隨機(jī)值。于是我敲了幾行代碼自己看一下結(jié)果,發(fā)現(xiàn)并非如此。代碼及輸出結(jié)果如下:
運(yùn)行結(jié)果:
直接報(bào)錯(cuò)!既不是0也不是隨機(jī)值。更不是結(jié)構(gòu)體里面變量是0,類里面成員變量是隨機(jī)值。這個(gè)基本可以確定是跟編譯器有關(guān)。因?yàn)槲铱吹哪瞧┛屠锩嬗写a和運(yùn)行結(jié)果,而且是2015年博客,編譯器肯定更加完善,發(fā)現(xiàn)有明顯不合適的使用時(shí),直接報(bào)錯(cuò)。
我覺得這些問題可以自己思考動(dòng)手實(shí)踐看結(jié)果。可以引發(fā)興趣,在網(wǎng)上查資料思考。但是一直糾結(jié)深究覺得沒必要,一般情況下,在C++里我們只把類(struct)看成對(duì)數(shù)據(jù)和方法封裝的東西。里面包括一些設(shè)計(jì)和實(shí)現(xiàn)。創(chuàng)建對(duì)象的時(shí)候,類才算配的上用場(chǎng),所以即使現(xiàn)在編譯器允許初始化,我們也一般情況下不初始化。創(chuàng)建對(duì)象的時(shí)候,再對(duì)對(duì)象的數(shù)據(jù)成員進(jìn)行初始化賦值等操作。(如果類里面有常變量或者其他必須定義的時(shí)候就初始化的變量,我們才給一個(gè)初始值)。
三、C++中空結(jié)構(gòu)體和類大小
直接用程序計(jì)算看一下:
struct A {};class B {}; int main() {A a;cout << sizeof(a) << endl;B b;cout << sizeof(b) << endl;return 0; }運(yùn)行結(jié)果:
空類和空對(duì)象的大小都是1(由于這里結(jié)構(gòu)體和類一樣,就單獨(dú)討論類)。
這是因?yàn)镃++中標(biāo)準(zhǔn)規(guī)定,任何不同的對(duì)象不能擁有相同的內(nèi)存地址。如果空類大小為0,文聲明這個(gè)類的對(duì)象數(shù)組,那么數(shù)組中每個(gè)對(duì)象內(nèi)存都是0,導(dǎo)致數(shù)組中的它們都擁有了相同的地址,這樣違背了C++標(biāo)準(zhǔn)。
所以,不只是類,其他任何類型,都不允許大小為0。所以編譯器為每個(gè)空類空結(jié)構(gòu)體都增加了一個(gè)虛設(shè)的字節(jié)(書上得知有的編譯器可能不止增加一個(gè)虛設(shè)字節(jié))。這樣就可以保證空對(duì)象和空結(jié)構(gòu)體的大小不會(huì)是0,保證了它們的對(duì)象擁有彼此獨(dú)立的空間。
還有之前聽到老師說的另一種解釋說是因?yàn)榭疹惱锩嬗心J(rèn)構(gòu)造函數(shù)和析構(gòu)函數(shù),所以大小為1字節(jié),但是我理解不了,查完資料之后我更相信上面C++標(biāo)準(zhǔn)規(guī)定的說法,對(duì)老師的這一個(gè)觀點(diǎn)存質(zhì)疑態(tài)度。
在這里提出對(duì)象必須擁有彼此獨(dú)立的空間,就想到了共用體。共用體是一種數(shù)據(jù)格式,能夠存儲(chǔ)不同的數(shù)據(jù)類型,但只能同時(shí)存儲(chǔ)其中一種數(shù)據(jù)類型。就想著共用體里有兩個(gè)對(duì)象會(huì)怎么樣,這個(gè)時(shí)候是兩個(gè)對(duì)象用同一個(gè)空間?這樣就違背了前面說的編譯器要求對(duì)象必須有獨(dú)立的空間,用共用體定義一個(gè)變量的時(shí)候,里面的對(duì)象會(huì)調(diào)用構(gòu)造函數(shù)嗎?如果都調(diào)用構(gòu)造函數(shù)的話,那就是兩個(gè)對(duì)象用一個(gè)空間了,如果為了避免兩個(gè)對(duì)象用一個(gè)空間而只選擇一個(gè)對(duì)象構(gòu)造的話,那么會(huì)選擇哪一個(gè)對(duì)象構(gòu)造呢?
突然想起很多,打開VS敲了一下代碼,一開始想要看一下共用體里面的兩個(gè)對(duì)象的構(gòu)造和析構(gòu)情況,我們使用共用體里面的對(duì)象的時(shí)候至少同時(shí)存在一個(gè)對(duì)象,所以至少會(huì)有一個(gè)對(duì)象調(diào)用構(gòu)造函數(shù),那么如果先構(gòu)造的對(duì)象還沒析構(gòu)就構(gòu)造了另一個(gè)對(duì)象的話,那么就產(chǎn)生了兩個(gè)對(duì)象同時(shí)存在一個(gè)空間,就違背了上面的理論。所以理想情況下,應(yīng)該是一個(gè)對(duì)象使用這個(gè)空間,另一個(gè)對(duì)象使用前,前一個(gè)對(duì)象一定會(huì)調(diào)用析構(gòu)函數(shù)銷毀,另一個(gè)對(duì)象才會(huì)調(diào)用構(gòu)造函數(shù),這樣就完美岔開了,就不會(huì)說兩個(gè)對(duì)象同時(shí)存在一個(gè)空間中。為了清楚的看到構(gòu)造析構(gòu)的過程和順序?qū)懗鋈缦麓a:
寫到UU t;這句代碼的時(shí)候就報(bào)錯(cuò)了,說“無法引用共用體的默認(rèn)構(gòu)造函數(shù)——它是已刪除的函數(shù)”。看起來是因?yàn)闃?gòu)造函數(shù)被我們重寫造成的。但是當(dāng)我把構(gòu)造函數(shù)刪除,只留下一個(gè)析構(gòu)函數(shù)的時(shí)候,還是會(huì)報(bào)這樣的錯(cuò)誤。當(dāng)把構(gòu)造函數(shù)和析構(gòu)函數(shù)都刪除才沒有報(bào)錯(cuò)。如下代碼:
class A { public:int m_val; }; int main() {union UU{A a;A b;};UU t;t.a.m_val = 10;t.b.m_val = 20;cout << t.a.m_val << endl;cout << t.b.m_val << endl;return 0; }運(yùn)行結(jié)果:
因?yàn)槲覀儾荒軐憳?gòu)造函數(shù)和析構(gòu)函數(shù),所以最后也沒得出什么結(jié)果。但是由于共用體要求里面的數(shù)據(jù)類型要求某一時(shí)刻只能有一個(gè)成員是當(dāng)前成員。所以應(yīng)該是a和b是不同時(shí)存在的,用共用體切換使用它們倆的時(shí)候,應(yīng)該一直不斷的伴隨著對(duì)象的構(gòu)造和析構(gòu),只不過我們沒辦法重寫構(gòu)造函數(shù)和析構(gòu)函數(shù)在里面添加輸出語句來觀察構(gòu)造和析構(gòu)過程。
總結(jié)
以上是生活随笔為你收集整理的struct和class之间问题(+引发的个人思考,和对共用体思考)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 类的使用注意事项
- 下一篇: 类的构造函数(分类和调用)