C 語言的技術性規則

2021-10-24 19:15:31 字數 3377 閱讀 2475

下面規則針對的問題是如何在c++裡表述各種事物,這裡不討論能表述什麼。

不隱式地違反靜態型別系統:每個物件在建立時就具有特定的型別,例如double、char*或dial_buffer等。如果以與物件的型別不一致的方式去使用它,那就是違背了型別系統。絕不允許這種情況發生的語言稱為是強型別的。如果一種語言能在編譯時確認所有違反型別系統的情況,那麼它就是強靜態型別的。

c++ 從c語言繼承了許多特徵,例如聯合、強制轉換和陣列,這就使它不可能在編譯時檢查出所有違反型別系統的情況。這就是說,你需要顯式使用聯合、強制轉換、陣列、明確不加檢查的函式引數,或者顯式使用不安全的c連線去違反這裡的型別系統。所有不安全特徵的使用都可以處理為(在編譯時)產生警告。更重要的是,目前的c++ 已經擁有了一些很好的語言特徵,採用它們更方便而又同樣有效,因此可以避免使用不安全的特徵。這方面的例子包括派生類(2.9節)、標準陣列模板(8.5節)、型別安全的連線(11.3節),以及動態檢查的強制轉換(14.2節)。由於與c語言相容的需要以及常見的實踐,維持目前這種狀態的路還很長也很困難,但大部分程式設計師已經採納了更安全的方式。

只要有可能,總在編譯時進行檢查。只要有可能,那些在處理單獨編譯單位時只能提供資訊而無法檢查的東西,都要在連線時檢查。最後,這裡還提供了執行時的型別資訊(14.2節)和異常機制(第16章),以幫助程式設計師處理編譯和連線都無法捕捉的錯誤條件。當然,在實踐中,還是編譯時檢查的代價更低,也更值得信賴。

為使用者定義型別提供與內部型別同樣好的支援:因為我們把使用者定義型別看作c++ 程式的核心,語言當然應該給它們盡可能多的支援。因此,例如「類物件只能在自由空間中分配」這樣的限制就是無法接受的了。對於例如complex這樣的算術型別,確實需要真正的區域性變數,這也就導致了對於面向值的型別(實在型別)的支援不但可以與內部型別媲美,甚至還超過了它們。

區域性化是好事情:人們寫一段**時總希望它是自足的,除了可能需要從其他地方得到一些服務。也希望能使用一種服務又不帶來過多的麻煩和干擾。另外,人們也需要為其他人提供函式和類等,同時不擔心其實現細節與其他人的**出現相互干擾。

c語言距離這些思想都非常遙遠,聯結器可以看到所有全域性函式和全域性變數的名字,這些名字會與同樣名字的其他使用互相衝突,除非顯式將它們宣告為static。任何名字都可以當作函式名使用而不必事先宣告。作為早年把結構成員名也看成全域性名的遺風,在乙個結構裡面宣告的結構也是全域性的。此外,預處理程式的巨集處理根本不考慮作用域問題,因此,只要改變了標頭檔案或編譯器的某些選項,程式正文中的任意一段字元都可能變成另外的任何東西(18.1節)。如果你想去影響某些看起來是區域性的**,或希望通過某些小的「區域性」修改影響整個世界的其他部分,上面這些東西的威力異常強大。公平地說,我認為這些東西對於理解和維護複雜的軟體具有破壞性。因此決心提供更好的隔離手段,以對抗從「其他地方」來的破壞,對能從自己的**中「引出」什麼東西提供更好的控制。

對於**區域性化、使訪問總是通過良好定義的介面進行而言,類是第一位的最重要的機制。巢狀類(3.12節和13.5節)和名字空間進一步擴充套件了區域性作用域和訪問權的顯式授予的概念。由於這些情況,在乙個系統裡的全域性資訊的總量大大減少了。

訪問控制使訪問區域性化,而且沒有因為完全的隔離而造成執行時間或儲存空間的額外開銷(2.10節)。抽象類使人可以以最小的代價得到最大程度的隔離(13.2節)。

在類和名字空間裡,人們可以將宣告和實現分開,這也非常重要,因為這樣做使人更容易看到乙個類到底做了些什麼,而不必不斷地跳過描述有關工作如何完成的函式體。允許在類宣告中寫inline函式,這樣,當上述分離不合適時,可以得到另一種區域性性。

最後,如果**中重要的塊能放進乙個螢幕,對於理解和操作也將大有裨益。c語言傳統的緊湊性在這方面很起作用,c++ 允許在需要使用的地方引進新變數(3.11.5節),也是在這個方向上前進了一步。

避免順序依賴性:順序依賴性很容易使人感到困惑,在重新組織**時也容易引進錯誤。人們都會注意到,語句將按定義的順序執行,但卻往往會忽視全域性宣告之間和類成員宣告之間的相互依賴性。過載規則(11.2節)和基類的使用規則(12.2節)都經過了特殊處理,避免其**現對順序的依賴性。理想情況是,如果交換兩個宣告的順序會導致另一種不同的意思,那麼這就應該是乙個錯誤。對於類成員的規則就是這樣(6.3.1節)。但是,對全域性宣告不可能做到這一點。c預處理程式可以通過巨集處理引進根本無法預期的病態依賴性,從而可能造成很大的破壞(18.1節)。

我在某個時候曾經表達過有關避免微妙的解析方式的願望,說:「幫助你下決心不應該是編譯器的事情」。換句話說,產生編譯錯誤將比產生某種含糊的解析更容易接受。多重繼承的歧義性規則是這方面的好例子(12.2節)。關於過載函式的歧義性規則是另乙個例子,它也說明了在相容性和靈活性的約束下,要做好這件事有多麼困難(11.2.2節)。

如果有疑問,就選擇該特徵的最容易說清楚的形式:這是在不同可能性之中做選擇的第二規則,使用起來很有技巧性,因為它有可能變成一種關於邏輯美的爭論,而且可能與熟悉不熟悉有關。寫出描述它的輔導材料和參考手冊,看看人們是否容易理解,是這個規則的一種實踐方式。這裡的乙個意圖就是簡化教學人員和維護人員的工作。還應該記住,程式設計師們不笨,不應該通過付出重要的功能方面的代價去換取簡單性。

語法是重要的(常以某些我們不希望的方式起作用):保證型別系統的一致性,一般而言,保證語言的語義清晰、定義良好,是最基本的東西。語法是第二位的,而且,看起來人們能學會去喜愛任何語法形式。

當然,語法就是人們看到的東西,也是語言最基本的使用者介面。人們喜愛某些形式的語法,並以某種奇特的狂熱去表達他們的意見。我認為,想改變這些東西,或者不顧人們對某些特定語法的對抗情緒去引進新的語義或設計思想,都是沒希望成功的。因此,c++ 的語法,在設法使其更合理和規範的同時,也盡可能避免去觸犯程式設計師們的成見。我的目標是逐漸使一些討厭的東西淡出,例如隱含的int(2.8.1節)和老風格的強制(14.3.1節)等,同時又盡可能地減少使用更複雜形式的宣告符語法(2.8.1節)。

我試著把重要操作做成很容易看見的東西。例如,老風格強制的乙個重要問題是它們幾乎不可見。此外,我也喜歡把語義上醜陋的操作在語法上也弄成醜陋的,與語義相匹配,例如病態的型別強制(14.3.3節)。一般說,也應該避免過分囉嗦。

清除使用預處理程式的必要性:如果沒有c預處理程式,c語言本身和後來的c++ 可能早就是死胎了。沒有cpp,它們根本就不能有足夠的表達能力和靈活性,不能處理重要專案中所需要完成的各種任務。但在另一方面,cpp醜陋低階的語義也使構造和使用更高階、更優雅的c程式設計環境變得過分困難、代價過分昂貴。

因此,必須針對cpp的每個基本特徵,找到符合c++ 語法和語義的替代品。如果這個工作能夠完成,我們就可能得到乙個更便宜的大大改進的c++ 程式設計環境。沿著這個方向,我們也將清除許多很難對付的錯誤的根源。模板(第15章)、inline函式(2.4.1節)、const(3.8節)和名字空間(第17章)都是在這個方向上留下的腳印。

技術性 OO語言知識

持續更新。c 篇 分布計算提高效率的庫及庫函式,比如fb的folly庫就有folly gen一大堆函式,e.g.auto results from ids get 0 as 本質上和e.g.pyspark的分布式計算的底層思想是一致的。python篇 python特別適用於搭data pipelin...

技術性 OO語言知識

持續更新。c 篇 分布計算提高效率的庫及庫函式,比如fb的folly庫就有folly gen一大堆函式,e.g.auto results from ids get 0 as 本質上和e.g.pyspark的分布式計算的底層思想是一致的。python篇 python特別適用於搭data pipelin...

技術性 Search知識

持續更新。fb search框架 php寫的browse layer,負責同client對話以及param處理和passing等。c 寫的backend,最大的一坨叫做topaggregator,主要負責三件事 a rewrite query to get better search results...