轉載自https://github.com/applenob/Cpp_Primer_Practice,看C++primer的時用的筆記,自己做了一些補充,感謝前人的總結
OOP:概述
- 面向物件程式設計(object-oriented programming)的核心思想是資料抽象、繼承和動態系結,
- 繼承(inheritance):
- 通過繼承聯系在一起的類構成一種層次關系,
- 通常在層次關系的根部有一個基類(base class),
- 其他類直接或者簡介從基類繼承而來,這些繼承得到的類成為派生類(derived class),
- 基類負責定義在層次關系中所有類共同擁有的成員,而每個派生類定義各自特有的成員,
- 對于某些函式,基類希望它的派生類個自定義適合自己的版本,此時基類就將這些函式宣告成虛函式(virtual function),
- 派生類必須通過使用類派生串列(class derivation list)明確指出它是從哪個基類繼承而來,形式:一個冒號,后面緊跟以逗號分隔的基類串列,每個基類前都可以有訪問說明符,
class Bulk_quote : public Quote{}; - 派生類必須在其內部對所有重新定義的虛函式進行宣告,可以在函式之前加上
virtual關鍵字,也可以不加,C++11新標準允許派生類顯式地注明它將使用哪個成員函式改寫基類的虛函式,即在函式的形參串列之后加一個override關鍵字,
- 動態系結(dynamic binding,又稱運行時系結):
- 使用同一段代碼可以分別處理基類和派生類的物件,
- 函式的運行版本由實參決定,即在運行時選擇函式的版本,
定義基類和派生類
定義基類
- 基類通常都應該定義一個虛解構式,即使該函式不執行任何實際操作也是如此,
- 基類通過在其成員函式的宣告陳述句前加上關鍵字
virtual使得該函式執行動態系結, - 如果成員函式沒有被宣告為虛函式,則決議程序發生在編譯時而非運行時,
- 訪問控制:
protected: 基類和和其派生類還有友元可以訪問,private: 只有基類本身和友元可以訪問,
定義派生類
- 派生類必須通過類派生串列(class derivation list)明確指出它是從哪個基類繼承而來,形式:冒號,后面緊跟以逗號分隔的基類串列,每個基類前面可以有一下三種訪問說明符的一個:
public、protected、private, - C++11新標準允許派生類顯式地注明它將使用哪個成員函式改寫基類的虛函式,即在函式的形參串列之后加一個
override關鍵字, - 派生類建構式:派生類必須使用基類的建構式去初始化它的基類部分,
- 靜態成員:如果基類定義了一個基類成員,則在整個繼承體系中只存在該成員的唯一定義,
- 派生類的宣告:宣告中不包含它的派生串列,
- C++11新標準提供了一種防止繼承的方法,在類名后面跟一個關鍵字
final,
型別轉換與繼承
- 理解基類和派生類之間的型別抓換是理解C++語言面向物件編程的關鍵所在,
- 可以將基類的指標或參考系結到派生類物件上,
靜態型別與動態型別:如果基類在實參中是參考或者指標的形式,那么就可以動態型別轉換,即基類可以被派生類表示,但如果是實參既不是參考也不是指標,則它的靜態型別和動態型別是一致的
- 不存在從基類向派生類的隱式型別轉換,
- 派生類向基類的自動型別轉換只對指標或參考型別有效,物件之間不存在型別轉換,
- 如果將一個派生類物件賦值或拷貝給基類物件,該操作只會運行基類成員的拷貝建構式或者賦值函式,而派生類中的資料會被切掉(sliced down)/當用一個派生類物件為一個基類物件初始化或賦值時,只有該派生類物件中的基類部分會被拷貝、移動或者賦值,它的派生類部分將會被忽略掉
存在繼承關系的型別之間的轉換規則
- 從派生類向基類的型別轉換只對指標或參考型別有效
- 基類向派生類不存在隱式型別轉換
- 和任何其他成員一樣,派生類想基類的型別轉換也可能會由于訪問受限而變得不可行
虛函式
-
使用虛函式可以執行動態系結,
-
OOP的核心思想是多型性(polymorphism),
- 參考或指標的靜態型別和動態型別不同這一事實正是c++支持多型的根本所在
-
當且僅當對通過指標或參考呼叫虛函式時,才會在運行時決議該呼叫,也只有在這種情況下物件的動態型別才有可能與靜態型別不同,
-
派生類必須在其內部對所有重新定義的虛函式進行宣告,可以在函式之前加上
virtual關鍵字,也可以不加, -
C++11新標準允許派生類顯式地注明它將使用哪個成員函式改寫基類的虛函式,即在函式的形參串列之后加一個
override關鍵字, -
如果我們想覆寫某個虛函式,但不小心把形參串列弄錯了,這個時候就不會覆寫基類中的虛函式,加上
override可以明確程式員的意圖,讓編譯器幫忙確認引數串列是否出錯, -
如果虛函式使用默認實參,則基類和派生類中定義的默認實參最好一致,
-
通常,只有成員函式(或友元)中的代碼才需要使用作用域運算子(
::)來回避虛函式的機制,
抽象基類
- 純虛函式(pure virtual):清晰地告訴用戶當前的函式是沒有實際意義的,純虛函式無需定義,只用在函式體的位置前書寫
=0就可以將一個虛函式說明為純虛函式, - 含有純虛函式的類是抽象基類(abstract base class),不能創建抽象基類的物件,
- 派生類建構式只初始化他的直接基類,也就是說派生類如果沒有自己的資料成員也要提供一個接受基類中引數的建構式,
訪問控制與繼承
- 受保護的成員:
protected說明符可以看做是public和private中的產物,- 類似于私有成員,受保護的成員對類的用戶來說是不可訪問的,
- 類似于公有成員,受保護的成員對于派生類的成員和友元來說是可訪問的,
- 派生類的成員或友元只能通過派生類物件來訪問基類的受保護成員,派生類對于一個基類物件中的受保護成員沒有任何訪問特權,
- 派生訪問說明符:
- 對于派生類的成員(及友元)能否訪問其直接基類的成員沒什么影響,
- 派生訪問說明符的目的是:控制派生類用戶對于基類成員的訪問權限(即類的使用者的權限,如果是private,則不能通過派生類直接呼叫基類中的資料成員),比如
struct Priv_Drev: private Base{}意味著在派生類Priv_Drev中,從Base繼承而來的部分都是private的,
- 友元關系不能繼承,
- 改變個別成員的可訪問性:使用
using, - 默認情況下,使用
class關鍵字定義的派生類是私有繼承的;使用struct關鍵字定義的派生類是公有繼承的,- 15.18:派生類公有的繼承基類的時候,用戶代碼才能使用派生類向基類轉換
繼承中的類作用域
- 每個類定義自己的作用域,在這個作用域內我們定義類的成員,當存在繼承關系時,派生類的作用域嵌套在其基類的作用域之內,
- 派生類的成員將隱藏同名的基類成員,
- 除了覆寫繼承而來的虛函式之外,派生類最好不要重用其他定義在基類中的名字,
名字查找與繼承
假設呼叫p->mem()或者obj.mem(),需要依次呼叫以下四個步驟:
- 首先確定p的靜態型別,因為我們呼叫的是一個成員,所以該型別必然是型別別,
- 在p的靜態型別對應的類中查找mem,如果找不到,則依次在直接基類中不斷查找直至到達繼承鏈的頂端,如果找遍了該類及其基類仍然找不到,則編譯器將報錯,
- 一旦找到了mem,就進行常規的型別檢查已確認對于當前找到的mem,本次呼叫是否合法,
- 假設呼叫合法,則編譯器將根據呼叫的是否是虛函式而產生不同的代碼:
- 如果mem是虛函式且我們是通過參考或指標進行的呼叫,則編譯器產生的代碼將在運行時確定到底運行該虛函式的哪個版本,依據是物件的動態型別
- 反之,如果mem不是虛函式或者我們是通過物件(而非指標或參考)進行的呼叫,則編譯器將產生一個常規函式呼叫,
建構式與拷貝控制
虛解構式
- 基類通常應該定義一個虛解構式,這樣我們就能動態分配繼承體系中的物件了,
- 如果基類的解構式不是虛函式,則
delete一個指向派生類物件的基類指標將產生未定義的行為, - 虛解構式將阻止合成移動操作,
合成拷貝控制與繼承
- 基類或派生類的合成拷貝控制成員的行為和其他合成的建構式、賦值運算子或解構式類似:他們對類本身的成員依次進行初始化、賦值或銷毀的操作,
- 如果基類中的默認建構式,拷貝建構式、拷貝賦值函式或者解構式是被洗掉的函式或者不可訪問,則派生類中對應的成員將是被洗掉的
- 如果在基類中有一個不可訪問或者洗掉掉的解構式,則派生類中合成的默認和拷貝建構式將是被洗掉的,因為編譯器無法銷毀派生類物件的基類部分
- 如果派生類確實需要移動操作,需要在基類中定義移動操作,只要有移動操作就必須有顯式的拷貝操作
派生類的拷貝控制成員
- 當派生類定義了拷貝或移動操作時,該操作負責拷貝或移動包括基類部分成員在內的整個物件,
- 派生類解構式:派生類解構式先執行,然后執行基類的解構式,
繼承的建構式
- C++11新標準中,派生類可以重用其直接基類定義的建構式,
- 如
using Disc_quote::Disc_quote;,注明了要繼承Disc_quote的建構式,
容器與繼承
- 當我們使用容器存放繼承體系中的物件時,通常必須采用間接存盤的方式,
- 派生類物件直接賦值給積累物件,其中的派生類部分會被切掉,
- 在容器中放置(智能)指標而非物件,
- 對于C++面向物件的編程來說,一個悖論是我們無法直接使用物件進行面向物件編程,相反,我們必須使用指標和參考,因為指標會增加程式的復雜性,所以經常定義一些輔助的類來處理這些復雜的情況,
文本查詢程式再探
- 使系統支持:單詞查詢、邏輯非查詢、邏輯或查詢、邏輯與查詢,
面向物件的解決方案
- 將幾種不同的查詢建模成相互獨立的類,這些類共享一個公共基類:
WordQueryNotQueryOrQueryAndQuery
- 這些類包含兩個操作:
eval:接受一個TextQuery物件并回傳一個QueryResult,rep:回傳基礎查詢的string表示形式,
- 繼承和組合:
- 當我們令一個類公有地繼承另一個類時,派生類應當反映與基類的“是一種(Is A)”的關系,
- 型別之間另一種常見的關系是“有一個(Has A)”的關系,
- 對于面向物件編程的新手來說,想要理解一個程式,最困難的部分往往是理解程式的設計思路,一旦掌握了設計思路,接下來的實作也就水到渠成了,
Query程式設計:
| 操作 | 解釋 |
|---|---|
Query程式介面類和操作 |
|
TextQuery |
該類讀入給定的檔案并構建一個查找圖,包含一個query操作,它接受一個string實參,回傳一個QueryResult物件;該QueryResult物件表示string出現的行, |
QueryResult |
該類保存一個query操作的結果, |
Query |
是一個介面類,指向Query_base派生類的物件, |
Query q(s) |
將Query物件q系結到一個存放著string s的新WordQuery物件上, |
q1 & q2 |
回傳一個Query物件,該Query系結到一個存放q1和q2的新AndQuery物件上, |
q1 | q2 |
回傳一個Query物件,該Query系結到一個存放q1和q2的新OrQuery物件上, |
~q |
回傳一個Query物件,該Query系結到一個存放q的新NotQuery物件上, |
Query程式實作類 |
|
Query_base |
查詢類的抽象基類 |
WordQuery |
Query_base的派生類,用于查找一個給定的單詞 |
NotQuery |
Query_base的派生類,用于查找一個給定的單詞 |
BinaryQuery |
Query_base的派生類,查詢結果是Query運算物件沒有出現的行的集合 |
OrQuery |
Query_base的派生類,回傳它的兩個運算物件分別出現的行的并集 |
AndQuery |
Query_base的派生類,回傳它的兩個運算物件分別出現的行的交集 |
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/476464.html
標籤:C++
上一篇:SMFL 筆記
