C++ Primer 第五版 第15章 面向對象程序設計

面向對象程序設計基于三個基本概念:數據抽象、繼承和動態綁定。

繼承和動態綁定對編寫程序有兩方面的影響:一是我們可以更容易地定義與其他類相似但不完全相同的新類;二是在使用這些彼此相似的類編寫程序時,我們可以在一定程度上忽略掉它們的區別。

一、OOP:概述

面向對象程序設計(object-oriented programming)的核心思想是數據抽象、繼承和動態綁定。通過使用數據抽象,我們可以將類的接口與實現分離;使用繼承,可以定義相似的類型并對其相似關系建模;使用動態綁定,可以在一定程度上忽略相似類型的區別,而以統一的方式使用它們的對象。

繼承

基類負責定義在層次關系中所有類共同擁有的成員,而每個派生類定義各自特有的成員。

在C++語言中,基類將類型相關的函數與派生類不做改變直接繼承的函數區分對待。對于某些函數,基類希望它的派生類各自定義適合自身的版本,此時基類就將這些函數聲明成虛函數(virtual function)。

舉例:定義一個名為Quote的類,并將它作為層次關系的基類。Quote的對象表示按原價銷售的書籍。Quote派生出另一個名為bulk_quote的類,它表示可以打折銷售的書籍。

派生類必須通過使用類派生列表(class derivation list)明確指出它是從哪個(哪些)基類繼承而來的。類派生列表的形式是:首先是一個冒號,后面緊跟以逗號分隔的基類列表,其中每個基類前面可以有訪問說明符:

派生類內部(成員函數或友元函數)使用基類成員時:不受繼承方式的影響,只看該成員在基類中的訪問屬性。

派生類外部(派生類用戶)使用基類成員時:不同的繼承方式決定了基類成員在派生類中的訪問屬性,從而對派生類用戶的訪問權限產生影響。

public繼承:所有基類成員在派生類中保持原有的訪問級別。

之后我們將繼續學習protected繼承和private繼承。

派生類必須在其內部對所有重新定義的虛函數進行聲明。派生類可以在這樣的函數之前加上virtual關鍵字,但也并不是非得這么做。C++11允許派生類顯式地注明它將使用哪個成員函數改寫基類的虛函數,具體措施是在該函數的形參列表之后增加一個override關鍵字。

動態綁定

因為在上述過程中函數的運行版本由實參決定,即在運行時選擇函數的版本,所以動態綁定有時又被稱為運行時綁定。

二、定義基類和派生類
1. 定義基類

成員函數與繼承

任何構造函數之外的非靜態函數都可以是虛函數。關鍵字virtual只能出現在類內部的聲明語句之前而不能用于類外部的函數定義。如果基類把一個函數聲明成虛函數,則該函數在派生類中隱式地也是虛函數。

成員函數如果沒被聲明為虛函數,則其解析過程發生在編譯時而非運行時。

訪問控制與繼承

派生類可以繼承定義在基類中的成員,但是派生類的成員函數不一定有權訪問從基類繼承而來的成員。和其他使用基類的代碼一樣,派生類能訪問公有成員,而不能訪問私有成員。不過有些時候基類中還有這樣一種成員,基類希望它的派生類有權訪問該成員,同時禁止其他用戶訪問。我們用受保護的(protected)訪問運算符說明這樣的成員。

2. 定義派生類

派生類必須通過類派生列表明確指出它是從哪個(哪些)基類繼承而來的。

類派生列表的形式是:首先是一個冒號,后面緊跟以逗號分隔的基類列表,其中每個基類前面可以有以下三個訪問說明符中的一個:public、protected或者private。

派生類必須將其繼承而來的成員函數中需要覆蓋的那些重新聲明。

訪問說明符的作用是控制派生類從基類繼承而來的成員是否對派生類的用戶可見。

派生類中的虛函數

如果派生類沒有覆蓋其基類中的某個虛函數,則該虛函數的行為類似于其他的普通成員,派生類回直接繼承其在基類中的版本。

派生類對象及派生類向基類的類型轉換

C++標準并沒有明確規定派生類的對象在內存中如何分布。

因為在派生類對象中含有與其基類對應的組成部分,所以我們能把派生類的對象當成基類對象來使用,而且我們也能將基類的指針或引用綁定到派生類對象中的基類部分上。

這種轉換通常稱為派生類到基類的類型轉換。和其他類型轉換一樣,編譯器會隱式地執行派生類到基類的轉換。這種隱式特性意味著我們可以把派生類對象或者派生類對象的引用用在需要基類引用的地方;同樣的,我們也可以把派生類對象的指針用在需要基類指針的地方。

派生類構造函數

盡管在派生類對象中含有從基類繼承而來的成員,但是派生類并不能直接初始化這些成員。和其他創建了基類對象的代碼一樣,派生類也必須使用基類的構造函數來初始化它的基類部分

派生類對象通過構造函數初始化列表來將實參傳遞給基類構造函數。

除非我們特別指出,否則派生類對象的基類部分會像數據成員一樣執行默認初始化。

派生類使用基類的成員

派生類可以訪問基類的公有成員和受保護成員。

繼承與靜態成員

如果基類定義了一個靜態成員,則在整個繼承體系只存在該成員的唯一定義。

派生類的聲明

派生類的聲明中包含類名但是不包含它的派生列表:

一條聲明語句的目的是令程序知曉某個名字的存在以及改名字表示一個什么樣的實體,如一個類、一個函數或一個變量等。

被用作基類的類

如果我們想將某個類用作基類,則該類必須已經定義而非僅僅聲明:

一個類是基類,同時它也可以是一個派生類·:

在這個繼承關系中,Base是D1的直接基類(direct base),同時是D2的間接基類(indirect base)。

每個類都會繼承直接基類的所有成員。

防止繼承的發生

C++提供了一種防止繼承發生的方法,即在類名后跟一個關鍵字final。

3. 類型轉換與繼承

通常情況下,如果我們想把引用或指針綁定到一個對象上,則引用或指針的類型應與對象的類型一致,或者對象的類型含有一個可接受的const類型轉換規則。存在繼承關系的類是一個重要的例外:我們可以將基類的指針或引用綁定到派生類對象上。例如,我們可以用Quote&指向一個Bulk_quote對象,也可以把一個Bulk_quote對象的地址賦給一個Quote*。

(可以將派生類當作基類來使用)

可以將基類的指針或引用綁定到派生類對象上有一層極為重要的含義:當使用基類的引用(或指針)時,實際上我們并不清楚該引用(或指針)所綁定對象的真實類型。該對象可能是基類的對象,也可能是派生類的對象。

靜態類型與動態類型

表達式的靜態類型(static type)在編譯時總是已知的,它是變量聲明時的類型或表達式生成的類型;動態類型(dynamic type)則是變量或表達式表示的內存中的對象的類型。動態類型直到運行時才可知。

基類的指針或引用的靜態類型可能與其動態類型不一致。

不存在從基類到派生類的隱式類型轉換

在對象之間不存在類型轉換

派生類向基類的自動類型轉換只對指針或引用類型有效,在派生類類型和基類類型之間不存在這樣的轉換。

當我們初始化或賦值一個類類型的對象時,實際上是在調用某個函數。當執行初始化時,我們調用構造函數;而當執行賦值操作時,我們調用賦值運算符。這些成員都包含一個參數,該參數的類型是類類型的const版本的引用。

三、虛函數

當我們使用基類的引用或指針調用一個虛成員函數時會執行動態綁定。因為我們直到運行時才能知道到底調用了哪個版本的虛函數,所以所有虛函數都必須有定義。通常情況下,如果我們不使用某個函數,則無須為該函數提供定義。但是我們必須為每一個虛函數都提供定義,而不管它是否被用到了,這是因為連編譯器也無法確定到底會使用哪個虛函數。

對虛函數的調用可能在運行時才被解析

當某個虛函數通過指針或引用調用時,編譯器產生的代碼直到運行時才能確定應該調用哪個版本的函數。被調用的函數是與綁定到指針或引用上的對象的動態類型相匹配的那一個。

派生類中的虛函數

派生類中虛函數的返回類型也必須與基類函數匹配。該規則有一個例外,當類的虛函數返回類型是類本身的指針或引用時,上述規則無效。也就是說,如果D由B派生得到,則基類的虛函數可以返回B*而派生類的對應函數可以返回D*,只不過這樣的返回類型要求從D到B的類型轉換是可訪問的。

final和override說明符

虛函數與默認實參

回避虛函數的機制

在某些情況下,我們希望對虛函數的調用不要進行動態綁定,而是強迫其執行虛函數的某個特定版本。通過作用域運算符可以實現這一目的。

四、抽象基類
純虛函數

純虛函數無須定義。我們通過在函數體的位置(即在聲明語句的分號之前)書寫 =0 就可以將一個虛函數說明為純虛函數。其中,=0只能出現在類內部的虛函數聲明語句處。

我們也可以為純虛函數提供定義,不過函數體必須定義在類的外部。我們不能在類的內部為一個=0的函數提供函數體。

含有純虛函數的類是抽象基類

含有(或者未經覆蓋直接繼承)純虛函數的類是抽象基類。我們不能(直接)創建一個抽象基類的對象。

派生類構造函數只初始化它的直接基類

五、訪問控制與繼承

每個類分別控制自己的成員初始化過程。與之類似,每個類還分別控制著其成員對于派生類來說是否可訪問(accessible)。

受保護的成員

一個類使用protected關鍵字來聲明那些它希望與派生類分享但不想被其他公共訪問使用的成員。

· 和私有成員類似,受保護的成員對于類的用戶來說是不可訪問的。

· 和公有成員相似,受保護的成員對于派生類的成員和友元來說是可訪問的。

· 派生類的成員或友元只能通過派生類對象來訪問基類的受保護成員。派生類對于一個基類對象中的受保護成員沒有任何訪問特權。

公有、私有和受保護繼承

某個類對其繼承而來的成員的訪問權限受到兩個因素影響:一是在基類中該成員的訪問說明符,二是在派生類的派生列表中的訪問說明符。

對基類成員的訪問權限只與基類中的訪問說明符有關。

派生訪問說明符的目的是控制派生類用戶(包括派生類的派生類在內)對于基類成員的訪問權限:

派生訪問說明符還可以控制繼承自派生類的新類的訪問權限:

派生類向基類轉換的可訪問性

友元與繼承

就像友元關系不能傳遞一樣,友元關系同樣也不能繼承。

改變個別成員的可訪問性

有時我們需要改變派生類繼承的某個名字的訪問級別,通過使用using聲明可以達到這一目的。

默認的繼承保護級別

默認情況下,使用class關鍵字定義的派生類是私有繼承的;而使用struct關鍵字定義的派生類是公有繼承的,

在使用struct關鍵字和class關鍵字定義的類之間唯一的差別就是默認成員訪問說明符及默認派生訪問說明符;除此之外,再無其他不同之處。

六、繼承中的類作用域

當存在繼承關系時,派生類的作用域嵌套在其基類的作用域之內。如果一個名字在派生類的作用域內無法正確解析,則編譯器將繼續在外層的基類作用域中尋找該名字的定義。

在編譯時進行名字查找

一個對象、引用或指針的靜態類型決定了該對象的哪些成員是可見的。即使靜態類型與動態類型可能不一致(當使用基類的引用或指針時會發生這種情況),但是我們能使用哪些成員仍然是由靜態類型決定的。

舉個例子,我們給Disc_quote添加一個新成員,該成員返回一個存有最小(或最大)數量及折扣價格的pair:

名字沖突與繼承

和其他作用域一樣,派生類也能重用定義在其直接基類或間接基類中的名字,此時定義在內層作用域(即派生類)的名字將隱藏定義在外層作用域(即基類)的名字。

通過作用域運算符來使用隱藏的成員

名字查找先于類型查找

如果派生類(即內層作用域)的成員與基類(即外層作用域)的某個成員同名,則派生類將在其作用域內隱藏該基類成員。即使派生類成員和基類成員的形參列表不一致,基類成員也仍然會被隱藏掉

虛函數與作用域

覆蓋重載的函數

七、構造函數與拷貝控制

如果一個類(基類或派生類)沒有定義拷貝控制操作,則編譯器將為它合成一個版本。這個合成的版本可以定義成刪除的函數。

1. 虛析構函數

繼承關系對基類拷貝控制最直接的影響是基類通常應該定義一個虛析構函數,這樣我們就嫩滑動態分配繼承體系中的對象了。

虛析構函數將阻止合成移動操作

如果一個類定義了析構函數,即使它通過=default的形式使用了合成的版本,編譯器也不會為這個類合成移動操作。

2. 合成拷貝控制與繼承
派生類中刪除的拷貝控制與基類的關系

移動操作與繼承

大多數基類都會定義一個虛析構函數。因此在默認情況下,基類通常不含有合成的移動操作,而且在它的派生類中也沒有合成的移動操作。

因為基類缺少移動操作會阻止派生類擁有自己的合成移動操作,所以當我們確實需要執行移動操作時應該首先在基類中進行定義。

3. 派生類的拷貝控制成員

定義派生類的拷貝或移動構造函數

當為派生類定義拷貝或移動構造函數時,我們通常使用對應的基類構造函數初始化對象的基類部分:

派生類賦值運算符

與拷貝和移動構造函數一樣,派生類的賦值運算符也必須顯式地為其基類部分賦值。

無論基類的構造函數或賦值運算符是自定義的版本還是合成的版本,派生類的對應操作都能使用它們。

派生類析構函數

在析構函數體執行完成后,對象的成員會被隱式銷毀。類似的,對象的基類部分也是隱式銷毀的。和構造函數及賦值運算符不同的是,派生類析構函數只負責銷毀由派生類自己分配的資源:

在構造函數和析構函數中調用虛函數

4. 繼承的構造函數

類不能繼承默認、拷貝和移動構造函數。如果派生類沒有直接定義這些構造函數,則編譯器將為派生類合成它們。

派生類繼承基類構造函數的方式是提供一條注明了(直接)基類名的using聲明語句。

繼承的構造函數的特點

和普通成員的using聲明不一樣,一個構造函數的using聲明不會改變該構造函數的訪問級別。例如,不管using聲明出現在哪兒,基類的私有構造函數在派生類中還是一個私有構造函數;受保護的構造函數和公有構造函數也是同樣的規則。

而且,一個using聲明不能指定explicit或constexpr。如果基類的構造函數是explicit或者constexpr,則繼承的構造函數也擁有相同的屬性。

八、容器與繼承

當我們使用容器存放繼承體系中的對象時,通常必須采取間接存儲的方式。

在容器中放置(智能)指針而非對象

當我們希望在容器中存放具有繼承關系的對象時,我們實際上存放的通常是基類的指針(更好的選擇是智能指針)。這些指針所指的動態類型可能是基類類型,也可能是派生類類型。

模擬虛拷貝

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/22070.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/22070.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/22070.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HTML靜態網頁成品作業(HTML+CSS)—— 金寶貝兒童教育機構介紹網頁(2個頁面)

🎉不定期分享源碼,關注不丟失哦 文章目錄 一、作品介紹二、作品演示三、代碼目錄四、網站代碼HTML部分代碼 五、源碼獲取 一、作品介紹 🏷?本套采用HTMLCSS,未使用Javacsript代碼,共有2個頁面。 二、作品演示 三、代…

Stable diffusion prompts 使用語法、參數講解、插件安裝教程

Stable diffusion prompts 使用語法、參數講解、插件安裝教程 本文基于 Stable diffusion WebUI 進行講解(安裝在 AutoDL 上,安裝在本地電腦上的也同樣適用本教程)。 初始界面: 文件目錄結構: 上圖紅框中的 4 個文件…

requests模塊編寫漏洞檢測工具

#嘗試使用python登錄pikachu爆破模塊 #發送post數據包,包含用戶名密碼,對接受到的響應進行判斷,如何為登錄成功 #爆破密碼 with open(passwor.txt,r) as f: passwordf.readlines() for i in password: data {username: admin, password: i, …

數據結構——算法和算法效率的度量

目錄 一、引言 二、算法 1 算法的基本概念 2 算法的復雜度 2.1 時間復雜度 2.1.1 概念 2.1.2 大O的漸進表示 3 算法的空間復雜度 3.1 概念 3.2 實例 4 實例分析 5 結論 一、引言 大家在寫代碼的時候有沒有發現寫同樣功能的代碼有多種不同的寫法,而不同的代…

51種企業應用架構模式詳解

01 什么是企業應用 我的職業生涯專注于企業應用,因此,這里所談及的模式也都是關于企業應用的。(企業應用還有一些其他的說法,如“信息系統”或更早期的“數據處理”。)那么,這里的“企業應用”具體指的是什…

[原型資源分享]經典產品餓了么UI模版部件庫

?部件庫預覽鏈接:https://f13gm0.axshare.com 支持版本: Axrure RP 8 文件大小: 3MB 文檔內容介紹 基本部件:表單樣式:12款、數據樣式:10款、服務樣式:6款、導航:5款、業務組件:7款、 模板…

python把簡體中文轉換為繁體中文

Python 可以使用第三方庫來將簡體中文(簡體中文)轉換為繁體中文(繁體中文)。一個常用的庫是 opencc-python-reimplemented,它是 Open Chinese Convert (OpenCC) 的 Python 實現,OpenCC 是一個開源的中文簡繁…

MySQL之查詢性能優化(三)

查詢性能優化 重構查詢的方式 在優化有問題的查詢時,目標應該是找到一個更優的方法獲得實際需要的記過——而不是一定總是需要從MySQL獲取一模一樣的結果集。有時候,可以將查詢轉換一種寫法讓其返回一樣的結果,但是性能更好。但也可以通過修…

Python魔法之旅-魔法方法(14)

目錄 一、概述 1、定義 2、作用 二、應用場景 1、構造和析構 2、操作符重載 3、字符串和表示 4、容器管理 5、可調用對象 6、上下文管理 7、屬性訪問和描述符 8、迭代器和生成器 9、數值類型 10、復制和序列化 11、自定義元類行為 12、自定義類行為 13、類型檢…

在Debian系統上賦予普通用戶ping 權限

在Debian系統上,普通用戶默認情況下沒有權限使用 ping 命令,因為它需要發送 ICMP 包,這通常需要 root 權限。為了允許普通用戶使用 ping,可以設置 ping 命令的 setuid 位。以下是具體的步驟: 查找 ping 命令的位置&am…

2024年度自貢市社會民生重大科技計劃項目申報要求、時間流程

一、申報要求 申報項目需符合以下申報要求和申報指南要求,申報資料需在“自貢市科技綜合業務服務平臺”中的“自貢市重點科技計劃項目管理系統”上傳。 (一)項目申報單位要求。 1.項目申報單位包括項目牽頭單位和項目合作單位。 2.多家單…

【Python】pyinstaller打包時添加詳細信息

在要被打包的py文件同級目錄新建version.txt,寫入以下內容 # UTF-8 # # For more details about fixed file info ffi see: # http://msdn.microsoft.com/en-us/library/aa381058.aspx # VSVersionInfo(ffiFixedFileInfo(filevers(1, 4, 0, 5),prodvers(1, 4, 0, 5…

SpringBoot使用RabbitMQ實現延遲隊列

SpringBoot使用RabbitMQ實現延遲隊列 需求和目標名詞解釋實現方式引入依賴添加配置文件配置類死信隊列消費者即時隊列消費者延遲消息發送結果注意 需求和目標 商城系統,用戶下單后若15分鐘內仍未完成支付,則自動取消訂單,若已支付&#xff0c…

重組蛋白的定量定性方法,你了解嗎?

重組蛋白的定量和定性分析是蛋白質工程和生物技術中至關重要的步驟,用于確保蛋白質的表達、純度和功能性符合預期。以下是小編整理的一些常用的方法以及實驗介紹,希望這些方法幫助研究人員詳細了解重組蛋白的特性。 主要的定性方法 1 WB(Wes…

AIGC 011-SAM第一個圖像分割大模型-分割一切!

AIGC 011-SAM第一個圖像分割大模型-分割一切! 文章目錄 0 論文工作1論文方法2 效果 0 論文工作 這篇論文介紹了 Segment Anything (SA) 項目,這是一個全新的圖像分割任務、模型和數據集。SA 項目是一個具有里程碑意義的工作,它為圖像分割領域…

基于springboot的多媒體素材庫源碼數據庫

基于springboot的多媒體素材庫源碼數據庫 近年來,信息化管理行業的不斷興起,使得人們的日常生活越來越離不開計算機和互聯網技術。首先,根據收集到的用戶需求分析,對設計系統有一個初步的認識與了解,確定多媒體素材庫…

迎七一黨史知識競賽答題怎么做

迎七一黨史知識競賽答題,不僅是對于黨史知識的檢驗,更是對于參賽者學習態度和綜合能力的考量。在參與這類競賽時,我們需要做好充分的準備,掌握一定的答題技巧,才能取得好的成績。 首先,我們要深入了解競賽…

FFmpeg播放器的相關概念【1】

播放器框架 相關術語 ?容器/文件(Conainer/File):即特定格式的多媒體文件,比如mp4、flv、mkv等。 ? 媒體流(Stream):表示時間軸上的一段連續數據,如一段聲音數據、一段…

UFS Explorer Professional Recovery: 如何從啟用了 mSATA 緩存的 Drobo 設備中恢復數據

天津鴻萌科貿發展有限公司是 UFS Explorer Professional Recovery 數據恢復軟件的授權代理商。 UFS Explorer Professional Recovery 數據恢復軟件提供綜合性的解決方案,用于解決復雜的數據恢復案例,包括那些采用特殊存儲技術的案例,或介質受…

上海亞商投顧:創業板指震蕩收漲 超70家ST股跌停

上海亞商投顧前言:無懼大盤漲跌,解密龍虎榜資金,跟蹤一線游資和機構資金動向,識別短期熱點和強勢個股。 一.市場情緒 滬指昨日震蕩震蕩,創業板指走勢稍強,盤中一度漲超1%,黃白二線分化嚴重。算…