目錄
面向過程和面向對象
面向過程編程(Procedural Programming)
面向對象編程(Object-Oriented Programming)
一、類的定義
類定義格式
類域
二、類的訪問限定符及封裝
訪問限定符
?封裝
三、實例化
實例化概念
對象大小
四、this指針
this指針的特性
面向過程和面向對象
- C 語言是一種典型的面向過程的編程語言,程序的設計主要圍繞著函數和數據結構展開,強調的是功能的實現過程。
- C++既支持面向過程編程,又支持面向對象編程。面向對象編程的核心概念包括類、對象、封裝、繼承和多態等,使得程序的組織和設計更加符合現實世界的模型,提高了代碼的可維護性和可擴展性。
面向過程編程(Procedural Programming)
面向過程編程是一種以過程為中心的編程思想。在面向過程編程中,程序被看作是一系列的函數或過程的集合,這些函數或過程按照一定的順序執行,以完成特定的任務。
優點:
- 性能高效:由于面向過程編程直接按照流程執行任務,不需要過多的對象創建和管理,因此在一些對性能要求較高的場景下,如底層系統編程、嵌入式編程等,具有較高的執行效率。例如在操作系統內核中,對進程調度、內存管理等功能的實現,使用面向過程編程可以更好地優化性能。
- 邏輯清晰:對于簡單的程序邏輯,按照流程逐步實現,代碼的邏輯結構清晰易懂,易于理解和維護。比如一個簡單的計算兩個數之和的程序,使用面向過程編程可以直接定義函數進行計算。
缺點:
- 可維護性差:當程序規模增大、功能復雜時,面向過程編程的代碼可能會變得難以維護和擴展。因為各個函數之間的耦合度較高,修改一個函數可能會影響到其他相關的函數。
- 代碼復用性低:代碼的復用通常是通過函數的調用實現,但是對于復雜的功能模塊,復用的難度較大,無法很好地對功能進行封裝和抽象。
面向對象編程(Object-Oriented Programming)
面向對象編程是一種以對象為中心的編程思想。對象是包含數據(屬性)和操作這些數據的方法(行為)的實體。通過將相關的數據和方法封裝在一個對象中,實現了數據和操作的一體化。
優點:
- 可維護性高:將功能封裝在對象中,使得對象的內部實現對外界隱藏,降低了模塊之間的耦合度。當需要修改功能時,只需要修改相應的對象內部實現,而不會影響到其他不相關的部分。例如,在一個圖形界面應用程序中,如果要修改某個按鈕的功能,只需要修改該按鈕對象對應的方法,而不會影響到其他界面元素。
- 代碼復用性強:可以通過繼承、多態等特性,方便地實現代碼的復用和擴展。例如,創建一個基類?
Shape
(形狀),然后派生出?Circle
(圓形)、Rectangle
(矩形)等子類,子類可以復用基類的屬性和方法,并進行特定的擴展。- 靈活性好:面向對象編程支持多態性,使得程序在運行時可以根據對象的實際類型動態地選擇執行相應的方法,增加了程序的靈活性和擴展性。
缺點:
- 性能開銷:由于對象的創建、方法調用等操作需要一定的開銷,在一些對性能要求極高的場景下,可能會影響程序的執行效率。
- 學習成本高:面向對象編程的概念和特性相對復雜,對于初學者來說,學習和理解的難度較大。
一、類的定義
類定義格式
? class為定義類的關鍵字,Data為類的名字,{}中為類的主體,注意類定義結束時后?分號不能省略。類體中內容稱為類的成員:類中的變量稱為類的屬性或成員變量;類中的函數稱為類的?法或者成員函數。
? 為了區分成員變量,?般習慣上成員變量會加?個特殊標識,如成員變量前?或者后?加_或者m開頭,注意C++中這個并不是強制的,只是?些慣例。
? C++中struct也可以定義類,C++兼容C中struct的?法,同時struct升級成了類,明顯的變化是 struct中可以定義函數,?般情況下還是推薦?class定義類。
? 定義在類?的成員函數默認為inline。
class Date { public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;} private:// 為了區分成員變量,?般習慣上成員變量// 會加?個特殊標識,如_ 或者 m開頭int _year; // year_ m_yearint _month;int _day; }; int main() {Date d;d.Init(2024, 3, 31);return 0; }
類域
? 類定義了?個新的作?域,類的所有成員都在類的作?域中,在類體外定義成員時,需要使?::作?域操作符指明成員屬于哪個類域。
? 類域影響的是編譯的查找規則,下?程序中Init如果不指定類域Stack,那么編譯器就把Init當成全局函數,那么編譯時,找不到array等成員的聲明/定義在哪?,就會報錯。指定類域Stack,就是知道Init是成員函數,當前域找不到的array等成員,就會到類域中去查找。
#include<iostream>
using namespace std;
class Stack
{
public:// 成員函數void Init(int n = 4);
private:// 成員變量int* array;size_t capacity;size_t top;
};
// 聲明和定義分離,需要指定類域
void Stack::Init(int n)
{array = (int*)malloc(sizeof(int) * n);if (nullptr == array){perror("malloc申請空間失敗");return;}capacity = n;top = 0;
}
int main()
{Stack st;st.Init();return 0;
}
二、類的訪問限定符及封裝
訪問限定符
? C++?種實現封裝的?式,?類將對象的屬性與?法結合在?塊,讓對象更加完善,通過訪問權限 選擇性的將其接?提供給外部的用戶使?。
? public修飾的成員在類外可以直接被訪問;protected和private修飾的成員在類外不能直接被訪問,protected和private是?樣的,以后繼承章節才能體現出他們的區別。
? 訪問權限作?域從該訪問限定符出現的位置開始直到下?個訪問限定符出現時為?,如果后?沒有 訪問限定符,作?域就到}即類結束。
? class定義成員沒有被訪問限定符修飾時默認為private
? struct默認為public
? ?般成員變量都會被限制為private/protected,需要給別?使?的成員函數會放為public。
?封裝
面向對象的三大特性:封裝、繼承、多態。
在類和對象階段,主要是研究類的封裝特性,那什么是封裝呢?
封裝:將數據和操作數據的方法進行有機結合,隱藏對象的屬性和實現細節,僅對外公開接口來 和對象進行交互。
封裝本質上是一種管理,讓用戶更方便使用類。比如:對于電腦這樣一個復雜的設備,提供給用 戶的就只有開關機鍵、通過鍵盤輸入,顯示器,USB插孔等,讓用戶和計算機進行交互,完成日 常事務。但實際上電腦真正工作的卻是CPU、顯卡、內存等一些硬件元件。
對于計算機使用者而言,不用關心內部核心部件,比如主板上線路是如何布局的,CPU內部是如 何設計的等,用戶只需要知道,怎么開機、怎么通過鍵盤和鼠標與計算機進行交互即可。因此計 算機廠商在出廠時,在外部套上殼子,將內部實現細節隱藏起來,僅僅對外提供開關機、鼠標以 及鍵盤插孔等,讓用戶可以與計算機進行交互即可。
在C++語言中實現封裝,可以通過類將數據以及操作數據的方法進行有機結合,通過訪問權限來 隱藏對象內部實現細節,控制哪些方法可以在類外部直接被使用。
三、實例化
實例化概念
? ?類類型在物理內存中創建對象的過程,稱為類實例化出對象。
? 類是對象進行?種抽象描述,是?個模型?樣的東西,限定了類有哪些成員變量,這些成員變量只 是聲明,沒有分配空間,?類實例化出對象時,才會分配空間。
? ?個類可以實例化出多個對象,實例化出的對象占?實際的物理空間,存儲類成員變量。打個??:類實例化出對象就像現實中使?建筑設計圖建造出房?,類就像是設計圖,設計圖規劃了有多 少個房間,房間大小功能等,但是并沒有實體的建筑存在,也不能住?,?設計圖修建出房?,房?才能住?。同樣類就像設計圖?樣,不能存儲數據,實例化出的對象分配物理內存存儲數據。
對象大小
分析?下類對象中哪些成員呢?類實例化出的每個對象,都有獨?的數據空間,所以對象中肯定包含成員變量,那么成員函數是否包含呢??先函數被編譯后是?段指令,對象中沒辦法存儲,這些指令存儲在?個單獨的區域(代碼段),那么對象中非要存儲的話,只能是成員函數的指針。再分析?下,對象中是否有存儲指針的必要呢,Date實例化d1和d2兩個對象,d1和d2都有各?獨?的成員變量 _year/_month/_day存儲各?的數據,但是d1和d2的成員函數Init/Print指針卻是?樣的,存儲在對象 中就浪費了。如果?Date實例化100個對象,那么成員函數指針就重復存儲100次,太浪費了。其實函數指針是不需要存儲的,函數指針是?個地址,調?函數被編譯成匯編指 令[call地址],其實編譯器在編譯鏈接時,就要找到函數的地址,不是在運行時找,只有動態多態是在運行時找,就需要存儲函數地址。
上?我們分析了對象中只存儲成員變量,C++規定類實例化的對象也要符合內存對?的規則。?
?內存對齊規則
?內存對齊規則和C語言的一模一樣? ? ? ? ? 參考文章:C語言計算內存對齊
第?個成員在與結構體偏移量為0的地址處。
? 其他成員變量要對?到某個數字(對齊數)的整數倍的地址處。
? 注意:對?數=編譯器默認的?個對齊數與該成員大小的較小值。
?VS中默認的對?數為8
? 結構體總大小為:最大對齊數(所有變量類型最?者與默認對?參數取最小)的整數倍。
? 如果嵌套了結構體的情況,嵌套的結構體對?到??的最?對?數的整數倍處,結構體的整體大小就是所有最?對齊數(含嵌套結構體的對齊數)的整數倍。
沒有成員變量要給1個字節,因為如果?個字節都不給,怎么表?對象存在過呢!所以這?給1字節,純粹是為了占位標識對象存在。?
四、this指針
this指針的特性
Date類中有Init與Print兩個成員函數,函數體中沒有關于不同對象的區分,那當d1調?Init和 Print函數時,該函數是如何知道應該訪問的是d1對象還是d2對象呢?那么這?就要看到C++給了?個隱含的this指針解決這里的問題
? 編譯器編譯后,類的成員函數默認都會在形參第?個位置,增加?個當前類類型的指針,叫做this指針。比如Date類的Init的真實原型為,void Init(Date* const this, int year, int month, int day) ? 類的成員函數中訪問成員變量,本質都是通過this指針訪問的,如Init函數中給_year賦值, this- >_year = year;
? C++規定不能在實參和形參的位置顯的寫this指針(編譯時編譯器會處理),但是可以在函數體內顯示使用this指針。
#include<iostream>
using namespace std;
class Date
{
public:// void Init(Date* const this, int year, int month, int day)void Init(int year, int month, int day){// 編譯報錯:error C2106: “=”: 左操作數必須為左值// this = nullptr;// this->_year = year;_year = year;this->_month = month;this->_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:// 這?只是聲明,沒有開空間int _year;int _month;int _day;
};
int main()
{// Date類實例化出對象d1和d2Date d1;Date d2;d1.Init(2024, 7, 1); // d1.Init(&d1, 2024, 7, 1);d1.Print(); // d1.Print(&d1);d2.Init(2024, 7, 10); // d2.Init(&d2, 2024, 7, 10);d2.Print(); // d2.Print(&d2);return 0;
}