C++ 程式設計:程式語言正規化
程式設計正規化 是一種基於不同概念的程式設計模型,它塑造了程式設計師設計、組織和編寫程式的方式。一個 多正規化程式語言 允許程式設計師選擇特定的單一方法或混合不同程式設計正規化的部分。C++ 作為一種多正規化程式語言,支援使用過程式或面向物件程式設計的單一或混合方法,並混合使用泛型甚至函數語言程式設計概念。
程序式程式設計 可以定義為 指令式程式設計 的一種子型別,它是一種基於過程呼叫的程式設計正規化,其中 語句 被組織成過程(也稱為子例程或函式)。過程呼叫是模組化的,並受作用域的約束。過程式程式由一個或多個 模組 組成。每個模組由一個或多個 子程式 組成。模組可以包含過程、函式、子例程或方法,具體取決於程式語言。過程式程式可能具有多個級別或作用域,子程式定義在其他子程式中。每個作用域可以包含在外部作用域中不可見的名稱。
程序式程式設計相對於簡單的順序程式設計提供了許多優勢,因為過程式程式碼
- 更容易閱讀,更容易維護
- 更加靈活
- 有利於良好的程式設計實踐
- 允許模組以 程式碼庫 的形式重複使用。
型別 指的是計算機語言如何處理其變數,以及它們如何透過 型別 來區分。變數是在程式執行期間程式使用的值。這些值可以改變;它們是可變的,因此得名。靜態型別 通常會生成執行速度更快的編譯程式碼。當編譯器知道正在使用的確切型別時,它可以生成更容易執行正確操作的機器程式碼。在 C++ 中,變數需要在使用之前進行定義,以便編譯器知道它們的型別,因此它是靜態型別的。非靜態型別的語言被稱為 動態型別。
靜態型別通常在編譯時更可靠地發現型別錯誤,從而提高編譯程式的可靠性。簡而言之,這意味著“圓 peg 不適合方形孔”,因此當型別導致歧義或不相容的使用時,編譯器會報告它。然而,程式設計師對型別錯誤的普遍程度以及靜態型別將捕獲多少已編寫錯誤存在分歧。靜態型別倡導者認為,當程式經過型別檢查時,它們會更可靠,而動態型別倡導者則指出已證明可靠的動態程式碼以及小型錯誤資料庫。因此,靜態型別的價值 presumably 隨著型別系統的強度增加而增加。
靜態型別系統比它限制不太強大的語言結構更限制強大語言結構的使用。這使得強大的結構更難使用,因此將選擇“適合問題的正確工具”的責任交給了程式設計師,否則他們可能傾向於使用最強大的工具。選擇過於強大的工具可能會導致額外的效能、可靠性或正確性問題,因為 理論限制 限制了可以從強大的語言結構中預期得到的屬性。例如,不加區別地使用 遞迴 或 全域性變數 可能會導致有據可查的不利影響。
靜態型別允許構建不太可能被使用者意外誤用的庫。這可以用作傳達庫開發人員意圖的額外機制。
型別檢查 是驗證和強制型別約束的過程,它可以在編譯時或執行時發生。編譯時檢查,也稱為 靜態型別 檢查,是在編譯程式時由編譯器執行的。執行時檢查,也稱為 動態型別檢查,是在程式執行時由程式執行的。如果型別系統確保型別之間的轉換必須有效或導致錯誤,則稱程式語言為 強型別。另一方面,弱型別 語言沒有這樣的保證,通常允許型別之間的自動轉換,這些轉換可能沒有有用的目的。C++ 處於中間位置,允許自動型別轉換和程式設計師定義的轉換,允許在解釋一種型別為另一種型別方面幾乎完全靈活。將一種型別的變數或表示式轉換為另一種型別稱為 型別轉換。
面向物件程式設計 可以看作是程序式程式設計的擴充套件,其中程式由稱為 物件 的單個單元的集合組成,這些單元具有不同的目的和功能,對 實現 的依賴性有限或沒有。例如,汽車就像一個物件;它可以將你從 A 點帶到 B 點,而無需知道汽車使用什麼型別的發動機或發動機的運作方式。面向物件的語言通常提供一種方法來 記錄 物件可以和不能做什麼,就像駕駛汽車的說明書一樣。
一個 物件 由 成員 和 方法 組成。成員(也稱為 資料成員、特徵、屬性 或 特性)描述了物件。方法通常描述與特定物件相關的操作。將物件視為名詞,其成員為描述該名詞的形容詞,其方法為可由該名詞執行或對該名詞執行的動詞。
例如,一輛跑車就是一個物件。它的成員可能包括高度、重量、加速度和速度。物件的成員只是儲存關於該物件的資料。跑車的一些方法可以是“駕駛”、“停車”、“比賽”等等。方法在與跑車關聯之前並沒有什麼意義,成員也是如此。
讓我們構建跑車物件的“藍圖”稱為類。類不會告訴我們跑車的速度有多快,或者它的顏色是什麼,但它會告訴我們,我們的跑車將有一個代表速度和顏色的成員,它們分別是一個數字和一個詞。類還會為我們制定方法,告訴汽車如何停車和駕駛,但這些方法僅憑藍圖無法採取任何行動——它們需要一個物件才能產生效果。
C++ 中的類與 C 中的結構相同;不同之處在於類使用者可以透過 private 選項隱藏資料。在 C++ 中,物件是類的例項,它被視為一個內建變數,其中包含多個值。
封裝是資訊隱藏(對使用者而言)的原則,是隱藏類資料結構並允許透過公共介面更改資料(在該介面中,傳入值會經過有效性檢查)的過程,因此它不僅允許隱藏物件中的資料,還允許隱藏行為。這防止了介面的客戶端依賴於將來可能發生更改的實現部分,從而使這些更改更容易進行,也就是說,無需更改客戶端。在現代程式語言中,資訊隱藏的原則以多種方式體現出來,包括封裝和多型性。
繼承描述了兩種(或多種)物件型別或類之間的關係,其中一種被稱為另一種的“子型別”或“子類”;因此,“子類”物件被認為繼承了父類的特性,從而允許共享功能。這使程式設計師能夠重複使用或減少程式碼,並簡化軟體的開發和維護。
繼承通常也包括子型別化,其中一種型別的物件被定義為另一種型別的更專門的版本(參見Liskov 替換原則),儘管非子型別繼承也是可能的。
繼承通常透過描述物件類按繼承層次結構(也稱為繼承鏈)排列來表達,繼承層次結構是由它們的繼承關係建立的樹狀結構。
例如,人們可以建立一個名為“哺乳動物”的可變類,它具有諸如進食、繁殖等特性;然後定義一個子型別“貓”,它繼承了這些特性,而無需顯式地對其進行程式設計,同時添加了諸如“追捕老鼠”等新特性。這允許不同型別物件之間的共性只表達一次並重復使用多次。
在 C++ 中,我們可以擁有與其他類相關的類(可以透過使用較舊的、預先存在的class來定義一個類)。這會導致一種情況,即新的類具有舊類所有的功能,並且還引入了它自己特有的功能。我們這裡不是指組合,即給定的類包含另一個類,而是指派生,即給定的類是另一個類。
當我們討論本書的類繼承部分中的類(和結構)繼承時,將進一步解釋這個 OOP 特性。
如果想要同時使用多個完全正交的層次結構,例如允許“貓”從“卡通人物”和“寵物”以及“哺乳動物”繼承,那麼我們正在使用多重繼承。
多重繼承是指一個類可以繼承兩個或多個類(分別稱為基類、父類、祖先類或超類)的屬性的過程。
這將在本書的C++ 類繼承部分中詳細介紹。
多型性允許為幾個相關但不同的目的重複使用同一個名稱。多型性的目的是允許使用一個名稱來表示一個通用類。根據資料型別,執行通用情況的特定例項。
多型性的概念更廣泛。每次我們使用具有相同名稱但實現不同的兩個函式時,就會出現多型性。它們也可能在介面上有所不同,例如,透過接收不同的引數。在這種情況下,選擇哪個函式由過載解析確定,並在編譯時執行,因此我們將其稱為靜態多型性。
動態多型性將在類部分中深入介紹,在那裡我們將討論它在派生類中重新定義方法時的使用。
泛型程式設計或多型性是一種程式設計風格,它強調允許一個值採用不同型別的技術,只要滿足某些契約,例如子型別和簽名。簡而言之,泛型程式設計是基於找到高效演算法的最抽象表示。模板普及了泛型的概念。模板允許編寫程式碼時不考慮最終使用它的型別。模板是在標準模板庫 (STL)中定義的,泛型程式設計就是在這裡引入到 C++ 中的。
自由格式指的是程式設計師如何編寫程式碼。基本上,除了 C++ 的語義規則之外,沒有關於如何選擇編寫程式的規則。只要是合法的 C++,任何 C++ 程式都應該可以編譯。
一些程式設計師使用 C++ 的自由格式性質來編寫混淆的 C++ 程式碼(故意編寫難以理解的程式碼)(或者說濫用,這取決於你的觀點)。在適當的上下文中,這也可以被視為一種工藝的展示(非功能性但對語言的藝術性控制),但一般來說,混淆的用途只被視為一種有用的原始碼安全機制,確保原始碼更難被第三方故意分析、複製或使用。如果對編譯器有足夠的瞭解,還可以設計原始碼,使其在編譯後的形式中保留“水印”,從而可以將它追溯到原始原始碼。