面向對象特色:抽象 封裝 繼承 多態數組
可重用型 software reusability函數
繼承 inheritancespa
做用:在一個已存在的類的基礎上創建一個新的類設計
已存在的類:父類 father class 基類 base class指針
新類:子類 son class 派生類 derived class對象
單繼承 single inheritance:一個派生類只從一個基類派生 樹結構繼承
多重繼承 multiple inheritance:一個派生類有兩個或以上基類 圖結構接口
派生類是基類的具體化ip
基類是派生類的抽象內存
派生類的聲明方式:
class 派生類名:[繼承方式] 基類名
{派生類新增長的成員};
繼承方式:
|--public 公用的
|--private 私有的(默認)
|--protected 受保護的
派生類的構成:
|--從基類繼承過來的成員 共性
|--本身增長的成員 個性
構造派生類的3個步驟:
一、從基類接受成員(不包括構造函數和析構函數) 謹慎定義基類,由於會形成冗餘
二、調整從基類接受的成員(公用變私有,重載)
三、增長派生類的個性成員,定義構造函數和析構函數
——————————————————————————————————
基類中的成員 公用派生類 私有派生類 保護派生類
私有成員 不可訪問 不可訪問 不可訪問
公用成員 公用 私有 保護
保護成員 保護 私有 保護
派生類中的成員有4種訪問屬性
派生類的成員 派生類中 派生類外部 下層派生類
公用成員 1 1 1
受保護成員 1 0 1
私有成員 1 0 0
不可訪問成員 0 0 0
私有繼承和保護繼承不經常使用
實際中經常使用的是公用繼承
———————————————————————————————————
派生類的構造函數
派生類不能繼承基類的構造函數和析構函數
在執行派生類的構造函數時,調用基類的構造函數
簡單派生類的構造函數
派生類構造函數名(總參數列表):基類構造函數名(參數列表) //相似於函數初始化列表
{ 派生類中新增數據成員初始化語句 }
先調用基類構造函數,再執行派生類構造函數
先執行派生類析構函數,再執行基類析構函數
有子對象 subobject 的派生類的構造函數
派生類構造函數名(總參數列表):基類構造函數名(參數列表),子對象名(參數列表)
{ 派生類中新增數據成員初始化語句 }
順序:
調用基類構造函數
調用子對象構造函數
執行派生類自己構造函數
對於多層派生的構造函數,只需寫出其上一層派生類/直接基類的構造函數便可
析構函數
先執行派生類本身的析構函數
再執行子對象的析構函數
最後執行基類的析構函數
———————————————————————————————————
多重繼承 multiple inheritance
容許一個派生類同時繼承多個基類
多重繼承派生類的構造函數
派生類構造函數名(總參數列表):基類1構造函數(參數列表),基類2構造函數(參數列表),基類3構造函數(參數列表)
{ 派生類中新增數據成員初始化語句 }
調用基類構造函數的順序與聲明順序相同
多重繼承引發的二義性 ambiguous
解決方法 在訪問時加 基類名::變量/函數
———————————————————————————————————
虛基類 virtual base class
A派生B C
D多繼承B C
虛基類讓D只保存一份A的數據
在繼承間接共同基類時,只保留一份成員
class A
{};
class B:virtual public A
{};
class C:virtual public A
{};
class D:public B, public A
{};
虛基類不是在聲明基類時聲明的,而是在聲明派生類時,指定繼承方式時聲明的
class 派生類名:virtual 繼承方式 基類名
爲保證虛基類在派生類中只繼承一次,應當在該基類的全部直接派生類中聲明爲虛基類。不然仍然會出現對基類的屢次繼承。
D的構造函數:D():A(),B(),C()
在最後的派生類中,負責對虛基類 直接基類的初始化
編譯系統只執行最後的派生類對虛基類的構造函數的調用,保證了虛基類的數據成員不會被屢次初始化
使用多重繼承要十分當心二義性問題。
不提倡在程序中使用多重啓程,能用單繼承就不用多重繼承。
———————————————————————————————————
基類與派生類的轉換
只有公用派生類纔是基類真正的子類型,它完整地繼承了基類的功能。
不一樣類型數據之間的自動轉換和賦值,稱爲賦值兼容
基類與派生類對象之間有賦值兼容關係
一、派生類對象能夠向基類對象賦值;
二、派生類對象能夠向基類對象的引用進行賦值或初始化;//此引用對應派生類中基類的部分
三、若是函數的參數是基類對象或基類對象的引用,相應的實參能夠用子類對象;
四、指向基類對象的指針能夠指向派生類對象。
——————————————————————————————————
組合 composition
在一個類中以另外一個類的對象做爲數據成員 has a 繼承 is a
———————————————————————————————————
繼承在軟件開發中的重要意義
縮短軟件開發過程的關鍵是 軟件重用
典型例子:類庫
對類庫中類的聲明通常放在頭文件中
類的實現(函數的定義部分)單獨編譯,以代碼形式存放在系統某一目錄下
——————————————————————————————————
多態性與虛函數
多態性 polymorphism
一個事物有多種形態
向不一樣的對象發送同一個消息,不一樣的對象在接受時會產生不一樣的行爲
從系統實現角度分類:
|--靜態多態性 編譯時多態 函數重載
|--動態多態性 運行時多態 虛函數 virtual function
虛函數的做用是容許在派生類中從新定義與基類同名的函數,
而且能夠經過基類指針或引用來訪問基類和派生類中的同名函數。
經過指向基類的指針,指向基類或不一樣的派生類對象,能夠調用各自的虛函數
virtual關鍵字
一、在基類中用virtual聲明成員函數爲虛函數,在類外定義虛函數時,沒必要再加virtual;
二、當一個成員函數被聲明爲虛函數後,其派生類中的同名函數都自動稱爲虛函數
建議在派生類的虛函數前加virtual
三、虛函數與指向基類對象的指針變量配合使用
函數重載:橫向 首部不一樣(參數不一樣)
虛函數:縱向 首部相同
編譯系統要根據已有的信息,對同名函數的調用作出判斷。
肯定調用的具體對象的過程稱爲關聯(binding)
把一個函數名與一個類對象捆綁在一塊兒,創建關聯
把一個標識符和一個存儲地址聯繫起來
靜態關聯 static binding
在編譯時便可肯定其調用的虛函數屬於哪一類
函數重載屬於靜態關聯 studl.diaplay() gradl.display()均屬於靜態關聯
運行前關聯 早期關聯 early binding
動態關聯 dynamic binding
在運行階段把虛函數和類對象binding在一塊兒
這種多態性是動態的多態性,運行階段的多態性
運行階段關聯 滯後關聯 late binding
說明:
使用虛函數,系統要有必定的空間開銷
當一個類帶有虛函數時,編譯系統會爲該類構造一個虛函數表(virtual function table)
它是一個指針數組,存放每一個虛函數的入口地址
系統在進行動態關聯時的時間開銷是不多的,所以多態是高效的。
———————————————————————————————————
若是析構函數未聲明爲虛函數
在撤銷動態存儲空間時,僅能調用基類的析構函數。
建議將基類的析構函數聲明爲虛函數
保證在撤銷動態存儲空間時能獲得正確處理。
———————————————————————————————————
純虛函數與抽象類
純虛函數 pure virtual function
在聲明虛函數時被「初始化」爲0的函數
通常形式:
virtual 函數類型 函數名(參數列表)=0;
注意:
一、純虛函數沒有函數體
二、最後面的「=0」僅起形式上的做用,告訴編譯系統「這是純虛函數」
三、聲明語句加分號
抽象類 abstract class
不用來定義對象而只做爲一種基本類型用做繼承的類
因爲它用做基類,一般稱爲抽象基類 abstract base class
凡是包含純虛函數的類都是抽象類
由於虛函數不能被調用,因此沒法創建對象
抽象類的做用是做爲一個類族的共同基類,爲一個類族提供一個公共接口。
能夠創建對象的類稱爲具體類 concrete class
———————————————————————————————————
異常處理
程序中兩大類錯誤:
|--編譯錯誤/語法錯誤
|--運行錯誤
|--除0
|--內存不足
|--沒法打開文件
|--數據類型出錯
在設計程序時,應當事先分析程序運行時可能出現的各類意外狀況,並分別制定相應的處理方法,這就是程序的異常處理的任務
若是在執行一個函數過程當中出現異常,能夠不在本函數中當即處理,而是發出一個信息傳給它的上一級(調用它的函數)
它的上級捕捉到這個信息後進行處理,若是處理不了,繼續向上級拋,、
若是最高一級也沒法處理,終止程序。
異常處理機制的3個部分:
try 檢查
catch 捕捉
throw 拋出
throw拋出什麼樣的數據由程序設計者自定,能夠是任何類型的數據(包括自定義類型的數據,如類對象,結構體)
說明:
一、被檢測的語句必須放在try塊中,不然不起做用
二、try塊和catch塊做爲一個總體出現,能夠有try無catch
三、try catch 後必須跟{}
四、只能有一個try,能夠有多個catch
五、catch後面的圓括號中,通常只寫異常信息的類型名
catch(double)
catch(double d) //能夠利用throw傳遞過來的具體值
六、catch(...) 捕捉任何類型的異常信息
七、throw;此語句表明:我不處理這個異常,請上級處理
九、若是throw拋出的異常信息找不到與之匹配的catch塊,那麼系統就會調用一個系統函數terminate,使程序終止運行
在函數聲明中進行異常狀況的指定
double triangle(double, double, doubl); //可拋任何類型的異常
double triangle(double, double, double) throw(double); //可拋double型異常
double triangle(double, double, double) throw(double, int, char, float); //可拋double int char float類型的異常
double triangle(double, double, double) throw(); //不能拋出異常
在異常處理中處理析構函數
C++的異常處理機制會在throw拋出異常信息被catch捕獲時,對有關的局部對象進行析構
析構對象的順序與構造的順序相反
而後執行與異常信息匹配的catch塊中的語句
——————————————————————————————————
命名空間
命名空間是能夠由用戶命名的做用域,用來處理程序中常見的同名衝突
所謂命名空間,實際上就是一個由程序設計者命名的內存區域。
程序設計者能夠根據須要指定一些有名字的空間域,把一些全局實體分別放在各個命名空間中,從而與其餘全局實體分隔開來。
namespace ns //namespace 關鍵字
{
int a;
double b; //a b 稱爲命名空間成員 namespace member
}
ns::a
ns::b //命名空間限定 qualified 被限定名 qualified name
命名空間的做用相似於目錄與文件的關係
命名空間的做用是創建一些相互分隔的做用域,把一些全局實體分割開來,以避免產生名字衝突。
全局變量在全局命名空間,獨立於全部有名的命名空間以外,它不須要用namespace聲明,由系統隱式聲明,存在於每一個程序之中。
命名空間能夠包括:
變量(能夠帶有初始化)
常量
函數(定義或者聲明)
結構體
類
模板
命名空間(嵌套命名空間)
使用命名空間成員的方法
一、命名空間名::命名空間成員名
二、使用命名空間別名
namespace 別名 = 命名空間名;
三、using 命名空間成員名
using std::cout;
四、using namespace 命名空間名
using namespace std;
無名的命名空間
file
namespace
{
void fun()
{...}
}
fun();
在無名的命名空間中的東西只在本文件的做用域中有效。C++
對全局變量用static聲明使其只在本文件有效。C
標準命名空間 std
存放標準庫的有關內容的命名空間
C++庫的全部標識符都是在一個名爲std的命名空間中定義的
或者說
標準頭文件中函數、類、對象、類模板是在命名空間std中定義的
C傳統方法 C++新方法
#include<stdio.h> #include<cstdio>
#include<math.h> #include<cmath>
#include<string.h> #include<cstring>
using namespace std;
無需命名空間 須要命名空間