2009年10月3日 星期六

C++ 中 struct 與 class keywords 的差異

C++ 中允許使用者在宣告(declare)、定義(define)型別時,使用 struct, class 兩個不同的關鍵字(keyword)。由於兩種都可以,因此很容易讓人好奇兩者是否存在必要性的差異。

在宣告時,使用 struct 或 class 都可以告訴 compiler 這是一個新的自訂型別,但在定義(define)時則有差異,根據最新 C++ standard
的描述, class 和 struct 對於 class definition 的差別在於:
1. 對於 member 的 default access level 不同(std.11);
2. 對於 base class 的default access level 不同(std.11.2)。

其不同在於 struct 是寬鬆的,都以 public 為預設。

上面的敘述很簡單,會引發一些聯想、甚至遐想:

Q1: 所以也可以使用 struct 寫多重繼承?多形?實現建構/解構子概念? 或者實作interface?就算可以,那為何會有class的產生?它不正是為了延伸structure的概念,達到更方便的OOP設計嗎?class 跟 struct 是可以互相繼承的嗎?
A1: 是的,可以。

Q2: 如果 Q1 的程式存成 .c ,可以compile過嗎?
A2: 沒辦法,C 語言不支援

Q3: 剛提到的差異是 class definition 上的,至於其他地方有差異嗎?
A3: 有的,像是 template 的 parameter list 只允許 class, typename ,無法使用 struct,因為 template 的設計,一開始就沒有打算和 C 相容。

Q4: 扣除些微差異,class 和 struct 幾乎是一樣的東西,為什麼 C++ 會同時引入兩個 keywords 呢?
A3: C++ 原本叫做: C with Class 。它的重要理念之一是與 C 相容,不過確實也是有不相容的地方,C++ Standard 的附錄 C 就列舉了 C++ 與 C 不相容的部份。因此為了這相容性,不可能將 struct keyword 拿掉,引入 class 這個新的 keyword ,它所帶來的不只是 parser 要多 parse 一個字,更重要的是其背後的抽象性、封裝性以及滿足人們對於 OO 的期待心理。而且以 Google 上"C++ struct class?"的搜尋,會先冒出一堆兩者的差異、比較的網頁就知道,class 的確辦到了"混淆視聽"的效果 : )

Q5: 都聽你在講,有沒有權威一點的說法啊?
A5:  Stanley Lippman 的著作: Inside The C++ Object Model,裡頭有一個章節叫做:Keywords, Schmeewords;開頭就是這樣說的:

One answer to the question of when, if ever, you should use a struct declaration rather than a class declaration then, is whenever it makes one feel better.

是的,重點在於 struct, class 定義出的 attributes, member functions 是否能符合你的需求期望。兩者的差異在於主觀的感覺: struct 用在 data aggregation,class 用在 Object oriented 或 object based 上。

Q6: 那為什麼不在 C++ 內限制 struct 只能宣告一般 aggregation type,不能有 access level 、member function 等能力呢?
A6: 其實原因很簡單:只是為了 C 到 C++ 遷移順利。 C++ 草創之初,除了效能是個大家關注的議題之外,什麼時候使用 struct 取代 class 也是常被問起的問題,與其涇渭分明的兩套標準,不如以一貫之,Bjarnet Stroustup 在 The Design and Evolution of C++ 這樣講著:

Maybe we could have lived with two set of rules, but a single concept provides a smoother integration of features and simpler implementation.尤其重要的是,嚴格區分兩者可能使得社群分裂(原文: community would fall into two distinct camps that would soon stop communicating.)

ㄜ,除了上面講的顏色對不對外,你覺得下面的 code 該編譯過還是不過呢?
// Forward Declaration
struct MyClass;
// Definition
class MyClass {
...
};
這應該是一致性問題,還是我們得一定要 compiler 嚴守 langauage 規範,幫我們挑出來呢? (以上很哲學又考古! )

Q7: 那我可以說 struct 原本要被幹掉嗎?
A7: 不算是要被幹掉,因為一開始就打算與 C 相容,所以不會幹掉,考量是:
1. 要不要引入新的 keywrod -- class
2. 引入 class 後,是否要讓 struct 與 class 不相容,使用兩套 rules。
而後的決定是:引入 class ,class 和 struct 使用同一套 rules (只差別在 default accessibility),而事實也證明 class 帶來令人滿意的效果,不管是心理的、實務上的。心理的上的話,套句 Lippman 的話:你會講 base class 還是 base struct ,雖然只是小小的哲學問題,卻還是得到滿足。

Q8: 我搞混了,那到底什麼時候該用 struct 、什麼時候該用 class?
A8: 我想 C++ 期望使用者自決。但我想可以分成兩個層面來看。先說心理層面:這是個 makes one feel better 的問題。
xxx MyClass {
public:
    void func();
private:
    int val_;
};
xxx 是 class or struct 都好,因為實際描述 MyClass 行為的是 class body 中的程式碼,而不是 struct 或 class。
另一個層面則比較實務:我想要 struct 或 class 定義出來的型別,其所產生的 object 與 C 是真正相容的,那就得考慮到 C++ 的 object model 和相關運算,簡單來說:要在 C++ 產生跟 C 相容的 object ,你的 C++ 型別必須是個 POD (Plain Old Data),standard 對於 POD 有其定義(std.9),或是上 Google 搜尋都可以看到,偷偷引用 wiki 的描述:
A POD type is a C++ type that has an equivalent in C, and that uses the same rules as C uses for
1. initialization
2. copying
3. layout
4. addressing
對我自己來說,什麼時候會使用 struct:
1. 想產生與 C 相容的 object 時,我會使用 struct 這個關鍵字,因為它帶有我對 C 的遐想,然後嚴格遵守 standard 對 POD 的規範。
2. 某些型別描述的對象很簡單(不太有(甚至沒有)繼承架構、抽象介面、生命週期短等),使用 setter, getter 又很麻煩時,那我會選使用 struct ,像是:
struct ParserResult {
bool Match;
int lineNo;
};
每個人都可以有自己一套結論!

Q8: 我試過了你這篇文章上弔詭(Paradox)的code:
struct MyClass;
class MyClass {};
/*
*warning C4099: 'MyClass' : type name first seen using 'struct' now  seen using 'class'
*
*是的,可以通過編譯,但會出現如此警告。
*因此將class、struct調換過來如下:
*/
class MyClass {};
struct MyClass;
/*
*錯誤訊息如下:
*warning C4099: 'MyClass' : type name first seen using 'class' now  seen using 'struct'
*/
A8: 揪甘心,這就是答案。記住:你的血統(class, struct)不代表什麼,重要的是你的所作所為(class body)