📙 作者簡介 :RO-BERRY
📗 學習方向:致力于C、C++、數據結構、TCP/IP、數據庫等等一系列知識
📒 日后方向 : 偏向于CPP開發以及大數據方向,歡迎各位關注,謝謝各位的支持
目錄
- 1.面向過程和面向對象初步認識
- 2.類的引入
- 3.類的定義
- 4.類的訪問限定符及封裝
- 4.1 訪問限定符
- 4.2 封裝
- 5.類的作用域
- 6.類的實例化
- 7.類對象模型
- 7.1 如何計算類對象的大小
- 7.2 類對象的存儲方式
- 7.3 結構體內存對齊規則
- 8.this指針
- 8.1 this指針的引出
- 8.2 this指針的特性
1.面向過程和面向對象初步認識
- C語言是面向過程的,關注的是過程,分析出求解問題的步驟,通過函數調用逐步解決問題。
在C語言里對于洗衣服就會分成一個一個過程
- C++是基于面向對象的,關注的是對象,將一件事情拆分成不同的對象,靠對象之間的交互完成。
2.類的引入
C語言結構體中只能定義變量,在C++中,結構體內不僅可以定義變量,也可以定義函數。比如:
之前在數據結構初階中,用C語言方式實現的棧,結構體中只能定義變量;現在以C++方式實現,會發現struct中也可以定義函數
C語言實現棧的代碼:
typedef int DataType;
struct Stack
{void Init(size_t capacity){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (nullptr == _array){perror("malloc申請空間失敗");return;}_capacity = capacity;_size = 0;}void Push(const DataType& data){// 擴容_array[_size] = data;++_size;}DataType Top(){return _array[_size - 1];}void Destroy(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}DataType* _array;size_t _capacity;size_t _size;
};
int main()
{Stack s;s.Init(10);s.Push(1);s.Push(2);s.Push(3);cout << s.Top() << endl;s.Destroy();return 0;
}
C++里面兼容C語言struct的所有用法
但是在C++中更喜歡用class來代替,這叫做類
3.類的定義
class className
{// 類體:由成員函數和成員變量組成
}; // 一定要注意后面的分號
class為定義類的關鍵字,ClassName為類的名字,{}中為類的主體,注意類定義結束時后面分號不能省略。
類體中內容稱為類的成員:類中的變量稱為類的屬性或成員變量; 類中的函數稱為類的方法或者成員函數。
類的兩種定義方式:
- 聲明和定義全部放在類體中,需注意:成員函數如果在類中定義,編譯器可能會將其當成內聯函數處理。
2. 類聲明放在.h文件中,成員函數定義放在.cpp文件中,注意:成員函數名前需要加類名::
一般情況下,更期望采用第二種方式。
成員變量命名規則的建議:
我們看看這個函數,是不是很僵硬?
class Date
{
public:void Init(int year){// 這里的year到底是成員變量,還是函數形參?year = year;}
private:int year;
};
所以一般都建議這樣
class Date
{
public:void Init(int year){_year = year;}
private:int _year;
};
或者這樣
class Date
{
public:void Init(int year){mYear = year;}
private:int mYear;
};
其他方式也可以的,主要看公司要求。一般都是加個前綴或者后綴標識區分就行
4.類的訪問限定符及封裝
4.1 訪問限定符
C++實現封裝的方式:用類將對象的屬性與方法結合在一塊,讓對象更加完善,通過訪問權限選
擇性的將其接口提供給外部的用戶使用
我們先來看一看類的工作方式:
#include<iostream>
using namespace std;
class Date
{
private:int _day;int _month;int _year;
public:void Init(int day,int month,int year){_day=day;_month=month;_year=year; }
}
關鍵字private和 public是新的,它們描述了對類成員的訪問控制。使用類對象的程序都可以直接訪問公有部分,但只能通過公有成員函數(或友元函數)來訪問對象的私有成員。
例如:要修改 Date 類的 day 成員,只能通過 Date 的成員函數。因此,公有成員函數是程序和對象的私有成員之間的橋梁,提供了對象和程序之間的接口。防止程序直接訪問數據被稱為數據隱藏(C++還提供了第三個訪問控制關鍵字protected,介紹類繼承時將討論該關鍵字。)
類設計盡可能將公有接口與實現細節分開。公有接口表示設計的抽象組件。將實現細節放在一起并將它們與抽象分開被稱為封裝。數據隱藏(將數據放在類的私有部分中)是一種封裝,將實現的細節隱藏在私有部分中。封裝的另一個例子是,將類函數定義和類聲明放在不同的文件中。下面我們會具體介紹封裝
【訪問限定符說明】
- public修飾的成員在類外可以直接被訪問
- protected和private修飾的成員在類外不能直接被訪問(此處protected和private是類似的)
- 訪問權限作用域從該訪問限定符出現的位置開始直到下一個訪問限定符出現時為止
- 如果后面沒有訪問限定符,作用域就到 } 即類結束。
- class的默認訪問權限為private,struct為public(因為struct要兼容C)
注意:訪問限定符只在編譯時有用,當數據映射到內存后,沒有任何訪問限定符上的區別
【面試題】
C++中struct和class的區別是什么?
C++需要兼容C語言,所以C++中struct可以當成結構體使用。另外C++中struct還可以用來
定義類。和class定義類是一樣的,區別是struct定義的類默認訪問權限是public,class定義的類
默認訪問權限是private。注意:在繼承和模板參數列表位置,struct和class也有區別,后序給大
家介紹。
4.2 封裝
【面試題】
面向對象的三大特性:封裝、繼承、多態。
在類和對象階段,主要是研究類的封裝特性,那什么是封裝呢?
封裝:將數據和操作數據的方法進行有機結合,隱藏對象的屬性和實現細節,僅對外公開接口來和對象進行交互。
**封裝本質上是一種管理,讓用戶更方便使用類。**比如:對于電腦這樣一個復雜的設備,提供給用戶的就只有開關機鍵、通過鍵盤輸入,顯示器,USB插孔等,讓用戶和計算機進行交互,完成日常事務。但實際上電腦真正工作的卻是CPU、顯卡、內存等一些硬件元件。
對于計算機使用者而言,不用關心內部核心部件,比如主板上線路是如何布局的,CPU內部是如何設計的等,用戶只需要知道,怎么開機、怎么通過鍵盤和鼠標與計算機進行交互即可。因此計算機廠商在出廠時,在外部套上殼子,將內部實現細節隱藏起來,僅僅對外提供開關機、鼠標以及鍵盤插孔等,讓用戶可以與計算機進行交互即可。
在C++語言中實現封裝,可以通過類將數據以及操作數據的方法進行有機結合,通過訪問權限來
隱藏對象內部實現細節,控制哪些方法可以在類外部直接被使用。
5.類的作用域
類定義了一個新的作用域,類的所有成員都在類的作用域中。在類體外定義成員時,需要使用 ::
作用域操作符指明成員屬于哪個類域。
在類中定義的名稱(如類數據成員名和類成員函數名)的作用域都為整個類,作用域為整個類的名稱只在改類中是已知的,在類外時不可知的。因此,可以在不同類中使用相同的類成員名而不會引起沖突。另外,類作用域意味著不能從外部直接訪問到類的成員,共有成員函數也是如此,也就是說,要調用公有成員函數,必須通過對象:
Date A; //創建類
Date.Init(2,4,2024); //調用類內部公有函數
Init(2,4,2024); //錯誤用法,查無此函數
同樣在類外定義成員函數時,必須使用作用域解析運算符:
void Date::show() //兩個連續冒號即是域解析運算符,前面跟上類的名稱即可
{...
}
使用類成員(類成員變量、類成員函數)時,必須根據上下文使用直接成員運算符
(.)
、間接成員運算符(->)
或作用域解析運算符(::)
。
6.類的實例化
用類類型創建對象的過程,稱為類的實例化
- 類是對對象進行描述的,是一個模型一樣的東西,限定了類有哪些成員,定義出一個類并沒有分配實際的內存空間來存儲它;比如:入學時填寫的學生信息表,表格就可以看成是一個類,來描述具體學生信息。類就像謎語一樣,對謎底來進行描述,謎底就是謎語的一個實例。謎語:“年紀不大,胡子一把,主人來了,就喊媽媽” 謎底:山羊
- 一個類可以實例化出多個對象,實例化出的對象會占用實際的物理空間,存儲類成員變量
int main()
{Person._age = 100; // 編譯失敗:error C2059: 語法錯誤:“.”return 0;
}
Person類是沒有空間的,只有Person類實例化出的對象才有具體的年齡
int main()
{Person A; //實例化對象A._age = 100; //運行成功return 0;
}
- 做個比方。類實例化出對象就像現實中使用建筑設計圖建造出房子,類就像是設計圖,只設
計出需要什么東西,但是并沒有實體的建筑存在,同樣類也只是一個設計,實例化出的對象
才能實際存儲數據,占用物理空間
7.類對象模型
7.1 如何計算類對象的大小
類的內存大小是多少呢?
我們前面學習過結構體的內存對齊規則,但是那是c語言
而C++又基本包含C語言的知識
那么C++類的大小是多少呢?
在這里a的大小是多少呢?
Init成員函數指針在不在內存大小里面呢?
結論是成員函數不在里面,類的大小只考慮成員對象的大小空間,成員函數不算成員對象。
那么成員函數為什么不在成員對象里面呢?
我們想調用函數是需要調用地址的
這里兩個不同類對象的Init函數所調用的地址是不是一樣的呢?
答案是一樣的
7.2 類對象的存儲方式
存儲方式如下圖:
類的成員對象是相互獨立的,但是對于一個類的成員函數它是放在一個公共區域的,就相當于一個小區,有很多戶人家,也就是實例化了很多個對象,但是在這個小區里籃球場,廣場只有一個,也就是公共區域,每家每戶都可以隨意使用。
在這里也就是我們可以每個實例化對象都可以訪問這個成員函數,但是這個成員函數是只初始化一次的,也就是放在公共區域,所以每一個類的實例化對象所計算內存大小只會計算成員對象,不會去計算成員函數,成員對象的內存計算就是該類中”成員變量”之和,當然要注意內存對齊
- 但是這對于空類卻不一樣
運行結果:
無成員變量的類,對象大小開一個字節,這個字節不存儲有效數據
標識定義的對象存在過
結論:
一個類的大小,實際就是該類中”成員變量”之和,當然要注意內存對齊
注意空類的大小,空類比較特殊,編譯器給了空類一個字節來唯一標識這個類的對象。
7.3 結構體內存對齊規則
- 第一個成員在與結構體偏移量為0的地址處。
- 其他成員變量要對齊到某個數字(對齊數)的整數倍的地址處。
注意:對齊數 = 編譯器默認的一個對齊數 與 該成員大小的較小值。
VS中默認的對齊數為8- 結構體總大小為:最大對齊數(所有變量類型最大者與默認對齊參數取最小)的整數倍。
- 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整
體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。
8.this指針
8.1 this指針的引出
我們先來定義一個日期類 Date
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
int main()
{Date d1, d2;d1.Init(2022, 1, 11);d2.Init(2022, 1, 12);d1.Print();d2.Print();return 0;
}
運行結果:
對于上述類,有這樣的一個問題:
Date類中有 Init 與 Print 兩個成員函數,函數體中沒有關于不同對象的區分,那當d1調用 Init 函
數時,該函數是如何知道應該設置d1對象,而不是設置d2對象呢?
C++中通過引入this指針解決該問題,即:C++編譯器給每個“非靜態的成員函數“增加了一個隱藏
的指針參數,讓該指針指向當前對象(函數運行時調用該函數的對象),在函數體中所有“成員變量”
的操作,都是通過該指針去訪問。只不過所有的操作對用戶是透明的,即用戶不需要來傳遞,編
譯器自動完成。
所以,系統會將如上代碼改成下面的這樣:其中有函數參數Date *this
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(Date *this){cout << this->_year << "-" <<this->_month << "-" << this->_day << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
int main()
{Date d1, d2;d1.Init(2022, 1, 11);d2.Init(2022, 1, 12);d1.Print(&d1);d2.Print(&d2);return 0;
}
注意:
我們是不能顯示的寫this參數的
8.2 this指針的特性
- this指針的類型:類類型* const,即成員函數中,不能給this指針賦值。
- 只能在“成員函數”的內部使用
- this指針本質上是“成員函數”的形參,當對象調用成員函數時,將對象地址作為實參傳遞給this形參。所以對象中不存儲this指針。
- this指針是“成員函數”第一個隱含的指針形參,一般情況由編譯器通過ecx寄存器自動傳遞,不需要用戶傳遞
【面試題】
- this指針存在哪里?
this指針是形參,是存在棧幀上面的,會被系統回收銷毀的,在vs里面this存在寄存器里