c++為什麼要讓struct可以定義成員函數?
好像功能上struct完全可以被class取代。如果只是為了前向兼容的話保留c的struct型式應該就可以了吧。從語義上看,struct是用來定義較為複雜的數據結構形式,而函數更像是一種方法,放在數據結構里好像意義上也說不太通。c++這樣設計感覺很奇怪。
因為C++的class真的只是個struct,只不過C++編譯器會在編譯期對它做一些「魔改」動作而已。
早年,諸如遊戲業的精靈抽象、unix系的「一切皆文件」等設計思想,都是用C乃至彙編表現出來的。正是這些思想啟發並引導了後來的面向對象熱潮。
對C來說,struct可以放任何它支持的數據類型。包括函數指針——後來C++所謂的虛函數表,很多編譯器的實現,就是一個指向另一個全局結構體的指針,這個全局結構體裡面是一條一條的函數指針,指向對應類重載後的所謂「虛函數」。
至於各種所謂的成員函數,實際上就是些第一個參數是classXX類型的this指針的普通函數。
換言之,struct本身已經足以支持面向對象編程運行期需要的一切了;現在,我們只需要增加一些新的語法約定,使得編譯器可以幫助用戶自動維護這些瑣碎細節就行了——這正是 Bjarne Stroustrup當年搞Cfront的設計思路: 先把C++代碼自動魔改到C,然後調用C編譯器完成編譯。
後期雖然有了直接支持c++的編譯器(因為在cfront中加入異常支持失敗),但編譯器關於struct的處理並沒有本質的不同。
於是,所謂class,其實就是加了某些外部支持的struct——除了編譯期可能會被編譯器魔改的面目全非外,它和普通struct毫無差別。
這個支持一般來說應該是這樣的(但並不禁止編譯器廠商選擇其他方案):
1、對普通成員函數,為它自動添加this參數,並在調用它時,自動把 obj.method() 轉換成 method(obj)格式;並識別出函數中涉及的、沒有顯式使用this的成員變數、為它加上this。除此之外,別的什麼都不用做。2、對虛函數,需要為繼承鏈上的每個類產生一個全局結構體,在這個結構體里按次序安排指向該類所有虛函數的指針,這就是虛函數表;然後在類里添加一個指向屬於自己的虛函數表的指針。
那麼,當用戶調用某個對象的第N個虛函數時,到虛函數表查找並獲取第N個函數指針指向的內容;然後類似調用普通成員函數一樣,把 obj.method() 轉換成 method(obj)格式,多態就實現了。當然,除此之外,還要在編譯時執行許可權檢查,避免非法訪問類的protect/private成員(struct默認許可權是public,class是private);以及另外一些瑣碎工作。
所以你看,c++的類歸根結底,它就是個C語言的struct。只是(相當於)在編譯期做了些預處理而已——只要你在裡面聲明了成員函數,就會自動觸發預處理,從而實現「自動魔改你所定義的數據結構,使其支持OO諸要素」功能。
那麼,除了讓C代碼共用C++裡面定義的struct這個特殊場景外(想了解這類能兼容C的數據類型,可搜索 POD類型),你說還有什麼理由禁止struct擁有成員函數呢?參與設計全世界第一套C++ 編譯器cfront的Lippman在《深度探索C++內存模型》中指出:
一個常被程序員詢問的問題是:什麼時候一個人應該在C++程序中以struct替代class?
如果是1986年,我的答案豪不拖泥帶水:「絕不!」……
那麼,讓我重新問一次:「什麼時候一個人應該使用struct取代class」答案之一是:當它讓一個人感覺比較好的時候。
雖然這個答案並沒有達到高技術水平, 但它的確指出了一個重要的持性,關鍵字struct本身並不一定要象徵其後隨之聲明的任何東西。我們可以使用struct代替 class,但仍然聲明public、private、protected等等存取區段,以及及一個完全 public的介面,以及 virtual functions, 以及単一繼承、多重繼承、虛擬繼承等等。 ……
最終我更改了語義分析器,使它同時接受兩個關鍵字——在沒有事先通知Bjarne和少不更事的ANSI C++委員會的情形下。……
如果C++要支持現存的C程序代碼,它就不能不支持struct。好的,那麼它需要引入新的關鍵字class嗎?真的需要嗎?不!但引入它非常令人滿意,因為這個語言所引入的不只是關鍵字,還有它所支持的封裝和繼承的哲學。……
你甚至可以主張說它的用途只是為了方便C程序員遷徙至C++ 部落。
(詳見《深度探索C++內存模型》)因為沒有C++之前,我們想用C引入一些面向對象的思想,我們就是在struct裡面弄一堆函數指針來使得struct的成員不僅有數據,還有方法。C++最大程度的繼承了C,class也是脫胎於struct,兩者並無本質區別,要說區別也就默認訪問許可權不同。
題主這個問題看似問了一個無關緊要的語言設計問題,但其實琢磨一下,C++的設計者也許是有一定深意的(基於猜測)
首先,我不贊同簡單地概括,C++中struct和class只是訪問許可權不同。還有一個要注意的點,就是memory layout有可能不同。struct的數據成員聲明順序和這些數據成員在內存中的offset一定是一致的(詳見[1])。而class只有在POD原則(Plain Old Data)下才與struct的memory layout一直(參見[2])。
所以,C++保留struct並不僅僅是保留一個languange上的keyword,同時也是保留了對low-level memory layout的控制。
回到原題的關鍵,而函數更像是一種方法,放在數據結構里好像意義上也說不太通
Q1: 為什麼structu要支持函數?
如果把struct對於函數的support去掉,那麼對於struct的操作都要使用外部函數,這不夠OOP。C++與C的關鍵不同就是在機制上引入OOP,如果struct不支持函數,那麼就是在鼓勵用戶使用struct做純數據結構,再使用外部函數,這在理念上不符合OOP的思想。
Q2: 為什麼無函數的純數據結構不夠OOP?在編程模型比較數學的表達里,有一個ADT的概念,可以說是object的一種數學抽象。In computer science, an abstract data type (ADT) is a mathematical modelfor data types where a data type is defined by its behavior (semantics) from the point of view of a user of the data, specifically in terms of possible values, possible operations on data of this type, and the behavior of these operations.
一個ADT是由數據(data)和操作(operation)共同構成的。編程語言的OOP程度,就在於對於data和operation的比重如何分配。如果把anti-OOP和純粹OOP作為尺度,anti-OOP是all data, zero operation。純OOP則是zero data, all operation,把內部成員全搞成private。
題主覺得struct+methods的設計不太好,就是因為它OOP的尺度上屬於中間騎牆派。struct的數據成員都是public暴露的,封裝性不好。
所以,struct+methods是一種妥協,一方面向C兼容,讓用戶在底層編程時能容易地控制memory layout。同時,又不願意看到method-less pure data structures,所以選擇了struct這種"有漏"的class設計。當然,這也符合大家對於C++的印象,工程能力強,OOP語言設計做了妥協。
[reference][1] C struct memory layout?[2] Structure of a C++ Object in Memory Vs a Struct為了把struct和class統一成一種東西吧。反過來問,為什麼不讓struct可以定義成員函數?這又沒有傷害到誰……
因為按照作者的意思,c++ 本來就想捨棄 struct ,只有 class。
但為了兼容 c,保留了 struct 關鍵字,等同於 class。(當然,struct 成員默認的是 public)。所以只需要了解,在 c++ 眼裡,struct 也是 class 就行了。在C++裡面struct和class唯二的不同就是訪問許可權不一樣,默認class是private struct是public和模版參數前面可以用class不可以用struct.struct和class都是對象,函數是對象溝通的消息,當然要放到對象裡面啦,當然放外面也是可以的,只是說你可以有這個做法。
用class定義的類,如果沒有說明則默認為private的,而用struct定義的,默認為public的。我個人認為struct與class是並行的,因為大多時候都會使用publish(至少競賽中是),所以用struct會簡潔一些,如果兼有private和publish,自然要用class。
推薦閱讀:
※如何把思維從面向過程轉向面向對象?
※C++通過基類指針delete派生類數組,析構函數是虛函數,程序為什麼會崩潰?
※面向對象和面向過程的區別有哪些?
※面向對象程序設計比傳統的面向過程程序設計更有什麼好處?
※面向對象編程是否是從根本上反模塊化且反並行的?為什麼?