OOP封裝:介紹了【類與對象】的概念,也介紹了其他相關的知識,如:【内聯函數】、【函數重載】、【this指針】等。
目錄
- 引言
- 類 & 對象
- 類的成員
- A)成員通路權限
- B)成員變量
- i.
auto
- ii.
decltype
- i.
- C)成員函數
- 補充知識
- 1. 内聯函數
- 2. 函數重載
- 3. this 指針
引言
面向對象程式設計(OOP),最核心的概念就是 “對象” 。
簡單地說,資料 + 函數 = 對象。
将一組資料和相關的函數結合起來,構成一個結合體,就是封裝的過程。
回到頂部
類 & 對象
類(class)是一種使用者自定義的類型,包含了資料和函數。
- 類中的資料 = 成員變量
- 在類内定義
- 類中的函數 = 成員函數
- 在類内聲明
- 可以在類内 / 類外實作
class Person { // 自定義的類 public: string name; // 成員變量 void get_name () { // 成員函數 cout << "Name: " << name; } }
- 定義了類後,将類當成一個資料類型,去定義一個變量,這個變量就是類的對象。
class Person { ...... } Person a; // a 是 Person 類的對象 a.name = "Karry"; // 使用對象的成員變量的方法 a.get_name(); // 調用對象的成員函數的方法
回到頂部
通常,為了友善管理,我們會将不同的類放在不同的頭檔案和實作檔案裡:
- 頭檔案:成員變量、成員函數的聲明
// 頭檔案 Person.h #fndef PERSON #define PERSON class Person { ...... } #endif
- 實作檔案:部分成員函數的實作
// 實作檔案 Person.cpp #include "Person.h" ...... // 部分函數實作
回到頂部
類的成員
類中的成員主要有:成員變量、成員函數
A)成員通路權限
通路權限分成三種等級:public、private 和 protected。
這裡不詳細說明三者的性質,隻需簡單了解,
public 的成員是可以在類外通路的,但是 private 的成員隻能在類内通路。
class Person {
int age; // 預設的通路權限為 private
public:
string name; // public 通路權限
}
int main() {
Person a;
a.name = "Karry"; // name 是 public 的成員變量,可以在類外通路
// a.age = 23; // age 是 private 的成員變量,沒有權限在類外通路
}
回到頂部
B)成員變量
成員變量的聲明方式和普通的變量相同。
除了常見的資料類型,如:
int
、
float
、
char
等,還有
auto
和
decltype
。
以下為
auto
和
decltype
的介紹,可适用于成員變量、成員函數、普通變量、普通函數等。
i. auto
auto
特點:
- 必須在編譯期确定類型
- 編譯器會根據上下文自動确定變量的類型
使用的注意事項:
- 定義的同時進行初始化
- 如果是用來定義一連串的資料,這些資料必須為同樣的資料類型
- 可用于:定義變量(尤其是複雜的類型)、追蹤函數的傳回類型
// 定義變量 auto num = 21; auto a = "Karry", b = "Wang"; // 追蹤函數的傳回類型 auto func(int a, int b) -> int { return a + b; }
- 不可用于:函數的參數
回到頂部
ii. decltype
decltype
特點:
- 可以對變量 / 表達式的類型進行推導
struct { string name; int id; } student; int main() { decltype(student) x; // 推導出 student 的類型,再用類型定義 x }
使用的方法:
- 結合
和auto
,可以自動追蹤函數的傳回類型decltype
auto func(int a, int b) -> decltype(a + b) { return a + b; } auto a = func(6, 9);
回到頂部
C)成員函數
成員函數的實作方式有:類内實作 / 類外實作
class Person {
public:
string name;
// 方法1:類内實作
void get_name() {
cout << "Name: " << name << endl;
}
}
// 方法2:類外實作
void Person::get_name() {
cout << "Name: " << name << endl;
}
定義在類内的成員函數,會被預設為内聯函數,是以大多數情況下,我們會将類的實作和聲明分開,便于管理。
有關内聯函數的更多說明可以在後文的 “補充知識“ 中找到。
關于成員函數,有一些延伸的知識,可以點選後方連結,跳轉到相關部分,如:
- 如果需要用到若幹個同名但是功能不同的函數,應該怎麼處理?[函數重載]
- 成員函數是如何使用成員變量的?[this 指針]
回到頂部
補充知識
1. 内聯函數
内聯函數(inline function)是一種特别的函數,可以在調用函數的地方生成與函數等價的表達式。例:
inline int min(int a, int b) {
return a < b ? a : b;
}
cout << min(2, 5) << endl;
以上代碼等價于:
cout << (a < b ? a : b) << endl;
簡單地說,内聯函數就是将函數的實作直接 ”植入“ 我們調用函數的地方。
雖然,類似的,我們也可以用宏定義(define)來實作這個功能,但是内聯函數和宏定義有着一定的差別。
内聯函數(inline) | 宏定義(define) | |
---|---|---|
操作 | 在函數調用的地方生成等價的表達式 | 将宏定義的代碼直接拷貝到目的地 |
功能 | 可以進行調試、執行檢查 | - |
安全 | 不容易出錯 | 容易出錯 |
使用内聯函數的注意事項:
- 内聯函數的聲明和實作不可以分開
- 如果函數包含大段代碼 / 循環 / 複雜結構,避免使用内聯函數
- 編譯器有決定權,内聯修飾符隻是一個建議:
- 如果函數不值得内聯,就會忽略内聯修飾符
- 如果覺得一些函數需要内聯,會将其自行轉換為内聯函數
- 定義在類内的成員函數都被預設為内聯函數
- 構造函數、析構函數通常都是内聯函數
- 大段 / 複雜的成員函數建議進行類外定義
回到頂部
2. 函數重載
函數重載:若幹個函數有着同樣的函數名(大機率功能是相似的),但是處理的資料資訊不同。
使用函數重載需要注意的事項:
- 函數的參數類型 / 數量需要有變化
- 函數的傳回類型 / 函數參數的名稱不能作為函數重載的區分辨別
- 如果函數參數有使用預設值,要避免出現二義性
預設值是函數參數中的預設值,例:
void print_num(int input = 2) { cout << "The number is " << input << endl; } print_num(5); // The number is 5 print_num(); // The number is 2
使用預設值時要注意,把參數是預設值的都往後放,避免混淆。
如果函數參數内有預設值時,要注意避免二義性的使用,例:
void func(int a, int b = 1) { cout << "The sum is " << a + b << endl; } void func(int a) { cout << "The number is " << a << endl; } int main() { //func(5); // 這種情況下會導緻函數重載失敗,無法區分兩個函數的使用 return 0; }
函數重載調用時的操作:
- 會優先調用類型比對的函數,例:
的資料就優先用int
參數的函數 ......int
- 如果沒有類型比對的函數,會進行内置類型轉換,例:
的轉去int
......float
void print_num(int a) { cout << "The number is " << a << endl; } print_num(5.2); // 會進行類型轉換,輸出 The number is 5
回到頂部
3. this 指針
類的成員函數,和普通的函數不同的是,它們可以使用類的成員變量。
這是因為,所有成員函數的參數中都有一個隐藏的指針變量
this
,指向目前對象,例:
void Person::get_name() {
cout << "The person name is " << this->name << endl;
}
回到頂部