在c++裡面,struct 和 class 有沒有區別?
有的!但是僅有一個,那就是struct的成員訪問的預設許可權是public,而class則是private的。
比如:
- class A
- {//a,b是private成員
- int a;
- int b;
- };
- struct B
- {//c,d是public成員
- int c;
- int d;
- };
就這一個區別而已!
那麼,為什麼還要保留這個struct呢,理由就是為了和C語言相容~,如果不是為了這個理由,c++中有很多東西都可以大量的簡化。
如果在聲明和定義時候使用不同的關鍵字,會產生什麼呢?比如:
- class A
- {//定義時候用class
- public:
- int a;
- };
- struct A aA; //使用時候用struct,注意,成員的預設訪問屬性是在定義中決定的,不是在聲明中!
- aA.a = 1;
在不同的編譯器可能會有不同的行為----在vs2005中,會產生一個警告;而在g++中什麼都沒有發生;
不過警告多了,畢竟還是不好的,尤其是在一大堆警告裡面含有幾個錯誤的情況(以前做項目時候這種情況出現,我簡直想打人....一百多個警告裡面,才有4個錯誤,當然,可以屏蔽警告,不過最好還是不要那麼做,否則,運行時出現問題了,你就要頭痛了,因為某些警告就是含蓄的告訴你一些執行階段錯誤的資訊,相信警告,他是你的朋友~)
那麼在C++中,什麼時候用struct呢?Lippman的答案是:在你想要的時候;就是說從編譯器的角度,兩者並沒有本質的區別,但是在使用者的習慣用法來看,可能會有微妙的區別----畢竟,一看到struct就可以想到這是C的東西,也就是如果是用C++的物件導向的東東來編程,用class可能會好一些;如果編寫的東西沒有用到物件導向的東西(比如封裝,繼承,多態),那就用struct吧。
當然有些公司的編程規範裡面也會有這方面的說明,具體問題還得具體分析,呵呵。
還有一個相關的話題,那就是有些在C語言中常用的技巧到了C++可能會變成陷阱。下舉兩個例子。
1、在C語言中,下面這個技巧是常用的:可變大小的數組,可以通過結尾僅含一個元素的char數組的結構體來實現(這說的都是什麼啊?舉個例子吧)比如,有如下代碼:
- struct A
- {
- int totalSize; //總的實際長度
- ... //其他成員
- char str[1]; //可變長的字串
- };
- char str[] = "abcdefg";
- struct A *pa = (struct A*) malloc( sizeof(struct A) * strlen(str) );
- pa->totalSize = sizeof(struct A) * strlen(str);
- strcpy(& pa->str, str);
- ...
其過程只可意會,不可言傳,呵呵(尷尬中,因為不知道怎麼表達)
那麼這樣做和通過指標作為成員的方法有什麼好處呢?該方法如下:
- struct A
- {
- int stringSize;
- char *pStr;
- };
- char str[] = "abcdefg";
- struct A *pa = (struct A*)malloc( sizeof(struct A) );
- pa->pStr = (char*)malloc( strlen(str) + 1);
- strcpy(pa->pStr, str);
- pa->stringSize = strlen(pa->pStr);
第一種做法的好處有二:一是struct A的成員char str佔了1個位元組,而第二種方法的指標佔了4個位元組;二是第一種方法訪問的時候夠直接,第二種方法訪問時候就多了一個間接關係(通過指標訪問多了一個間接,從上面的代碼就可以看出來了)。
這兩個好處在系統級和嵌入式方面的編程裡面是非常重要的,所以對於這個技巧也用的比較廣。
但是,在C++中,這個是一個陷阱----一個讓你頭暈的陷阱。
因為C++的編譯器有可能在類class A的物件版面配置裡面插入一些東西,比如當要實現多態的時候,會向每個對象插入一個vptr(指向虛函數表的指標),插入的位置因編譯器而異(在g++中是插入在最前面,在vs2005中是插入在最後面),當它插入的是最後面的時候,那個技巧很可能就泡湯了。(而且,段錯誤會不定時的發生----當你調用虛函數的時候,段錯誤才會發生,還有其他一些我不知道的情況...)。而且我敢說,一旦出現這樣的問題,調試過程會相當的艱難,因為錯誤的地方距離問題出現的地方何止十萬裡~
因此,這種技巧在c++中充滿了危險,至少也是不可移植的~
還有類似的“陷阱”,比如
- struct A
- {
- ...
- };
- struct A aA;
- memset( &aA, 0, sizeof(struct A) );
這個被在下評為C語言中將變數清0的最有效率之一的memset做法,到了C++中,如果你還這麼寫,那麼,我只能祝你好運~~----因為這種做法和上面的例子一樣,充滿了危險,尤其是當類裡面有虛函數的時候,那個時候,編譯器是要往對象裡面放東西的,現在你把這個東西清0了~,到某個時候再通過這個對象調用class A的虛函數試試你會哭不?(還有虛基類也是如此,略)
下面為了方便,
特例提供一下例子,以供娛樂(本例子僅供娛樂之用,如有別的效果,本人不負任何責任,呵呵):
- #include <iostream>
- using namespace std;
- class A
- {
- public:
- virtual void f(){ cout << "aaaaaaaa" << endl;}
- private:
- int a;
- };
- int main()
- {
- A aA;
- memset(&aA, 0, sizeof(A)); //把aA的指向虛函數表的指標也清0了
- A *pa = &aA;
- pa->aA; //用到了0的記憶體,出錯了
- return 0;
- }
這篇筆記就說到這裡吧,老樣子,還是請各位可以多多指教,謝謝。