往篇內容:
?C++學習筆記(一)
????????一、C++編譯階段※????????二、入門案例解析?
????????三、命名空間詳解
????????四、C++程序結構
C++學習筆記(二)
????????五、函數基礎
??????? 六、標識符
????????七、數據類型
????????補充:二進制相關的概念
? ? ? ? ? ? ? ? ? sizeof 運算符簡介
????????補充:原碼、反碼和補碼
C++學習筆記(三)
????????補充:ASCII碼表
????????八、內存構成
?????????補充:變量
????????九、指針基礎?
?????? ?十、const關鍵字
????????十一、枚舉類型?
C++學習筆記(四)
????????十二、 類型轉換
????????十三、define指令?
???? ? ?十四、typedef關鍵字
???? ? ?十五、運算符
????????十六、 流程控制
C++學習筆記(六)
? ? ? ? 十七、數組
C++學習筆記(七)
? ? ? ? 十八、指針
C++學習筆記(八)
????????十九、函數進階
? ? ? ? 二十、變量進階
C++學習筆記(九)
? ? ? ? 二十一、結構體
? ? ? ? 二十二、聯合
目錄
二十三、類與對象
1、面向過程
2、面向對象
?3、類
4、對象
5、成員引用
?6、成員函數
?7、inline函數
?8、對象內存
?9、this指針
10、類與結構體的區別?
11、封裝特性?
?
二十三、類與對象
1、面向過程
????????面向過程程序設計(Procedural-Oriented Programming, POP)是一種以“過程”為中心的編程思想。它強調的是解決問題的步驟和流程,通過函數(或稱為過程)來組織代碼。
????????在 C++ 中,雖然支持 OOP,但仍然兼容傳統的面向過程編程方式,例如使用全局變量、函數等。
思想:“先做什么,再做什么”,即按照順序執行一系列操作來完成任務。
特點:?
特性 | 描述 |
---|---|
程序結構 | 由主函數調用多個函數組成 |
數據與行為分離 | 數據和操作數據的函數是分開的 |
順序執行為主 | 強調流程控制,按步驟執行 |
全局變量 | 常用于共享數據 |
函數重用 | 可以通過函數復用代碼邏輯 |
示例:
#include <iostream>
using namespace std;void add(int a, int b) {cout << "Sum: " << a + b << endl;
}int main() {int x = 5, y = 10;add(x, y); // 調用函數return 0;
}
2、面向對象
????????面向對象程序設計(Object-Oriented Programming, OOP)是一種以“對象”為核心的編程思想。它將現實世界中的事物抽象為對象,每個對象包含數據(屬性)和對數據的操作(方法)。
????????C++ 是一種多范式語言,其中最強大的特性就是對 OOP 的完整支持。
思想:“萬物皆對象”,程序由一組相互協作的對象構成,每個對象封裝了自身的狀態和行為
四大核心特性:?
概念 | 描述 |
---|---|
封裝(Encapsulation) | 將數據和操作封裝在一個類中,對外隱藏實現細節 |
繼承(Inheritance) | 子類可以繼承父類的屬性和方法,實現代碼復用 |
多態(Polymorphism) | 同一個接口可以有多種實現,運行時決定具體行為 |
抽象(Abstraction) | 提取共性特征,忽略復雜實現細節 |
示例:
#include <iostream>
using namespace std;class Rectangle {
private:int width, height;public:Rectangle(int w, int h) : width(w), height(h) {}int area() {return width * height;}
};int main() {Rectangle rect(5, 10);cout << "Area: " << rect.area() << endl;return 0;
}
?3、類
????????類(class) 是用戶自定義的數據類型,用于封裝數據(屬性)和操作這些數據的方法(行為)。它是面向對象編程(OOP)的核心概念之一。
????????你可以把類看作是“藍圖”或“模板”,而對象則是根據這個模板創建出來的具體實例。
定義格式:?
class 類名{
????????成員訪問限定符: //private,public,protected三種取值
????????????????[數據成員s];
????????????????[成員函數s];
};
?成員訪問限定符:用來指定個數據成員的訪問屬性
- public :類內類外都可以訪問
- private :只能被本類的成員函數引用,類外不能調用(特殊情況后面再討論)
- protected :和private類似,派生時情況不同
注意事項:
- 成員訪問限定符可以出現多次
- 作用范圍:從當前限定符到下一個限定符或到類的 }
示例:
class Student {
private:
????????string name;
????????int age;
public:
????????void setName(string n) { name = n; }
????????void setAge(int a) { age = a; }
????????void printInfo() {
????????????????cout << "Name: " << name << ", Age: " << age << endl;
????????}
};
注意事項:?
- 聲明或定義類類型時不分配內存空間,用類類型實例創建對象時,分配內存空間
- 不指定成員限定符,默認為 private
- 定義類對象的方式有3種,與結構體類似
- 類的成員函數只能由類的對象或指針調用(特殊情況后續討論)
4、對象
- 客觀世界中任何事物都可以看成對象,對象由屬性和方法組成,每個對象都具有靜態的屬性和動態的功能。
- 具有相同屬性和功能的對象,我們可以把它們歸為一類,故而我們可以得出結論:類是對象的抽象,對象是類的實例。
抽象概念理解:抽象的過程是將有關事物的共性歸納、集中的過程。
定義對象格式:
class 類名 對象名;
類名 對象名; //推薦
//定義類的同時實例化對象
class 類名{
????????數據成員s;
????????成員函數s;
}對象1,對象2...對象n;
//定義匿名類,同時實例化對象
class {
????????數據成員s;
????????成員函數s;
}對象1,對象2...對象n;
案例:
#include <iostream>
#include <cstring>
using namespace std;// 定義類類型
class Student {// 默認權限修飾符為 private// 成員函數,一般 public
public:void disp() {cout << "id: " << id << endl;cout << "name: " << name << endl;cout << "age: " << age << endl;}// 權限修飾符,一般數據成員 private// private:
public:int id;char name[20];int age;
} s3 = {1020, "lucy", 21}; // 列表初始化依舊有用int main() {// 由類類型實例化對象【定義變量】Student s1;s1.id = 1001;// s1.name = "tom"; error,詳見字符串數組strcpy(s1.name, "tom");s1.age = 20;// 類對象調用成員函數Student s2 = s1; // 修正:刪除了多余的class關鍵字s2.disp();cout << "--------------" << endl;s3.disp();return 0;
}
?注意:對象初始化可以采用列表的方式進行,數據成員不能為private。
5、成員引用
使用類對象調用數據成員或成員函數有2種方式:
- 對象名.成員名;
- 類指針變量->成員名;
案例:
#include <iostream>
using namespace std;// 自定義結構體類型
class Student {
public:int num;char name[20];char sex;int age;float score;char addr[30];public:// 成員函數必須通過類對象或指針來調用// 對象名.disp(); 指針變量->disp();void disp() {cout << "Student: [" << num << "," << name << "," << sex<< "," << age << "," << score << "," << addr << "]" << endl;}
};// 值傳遞
void printStudent(Student s) {cout << "Student: " << s.num << "," << s.name << "," << s.sex << "," << s.age << "," << s.score << "," << s.addr << endl;
}// 指針傳遞
void printStudent(Student *p) {cout << "Student: {" << p->num << "," << p->name << "," << p->sex << "," << p->age << "," << p->score << "," << p->addr << "}" << endl;
}// 引用傳遞(注意:不能與值傳遞同名,會產生二義性)
void printStudent2(Student &s) {cout << "Student: (" << s.num << "," << s.name << "," << s.sex << "," << s.age << "," << s.score << "," << s.addr << ")"<< endl;
}int main() {// 初始化Student s = {2001, "Tom", 'M', 19, 78.5, "平遙學院路56號"};printStudent(s); // 值傳遞printStudent2(s); // 引用傳遞Student *ps = &s;printStudent(ps); // 指針傳遞cout << "---------------------" << endl;s.disp(); // 成員函數調用return 0;
}
?6、成員函數
????????類的成員函數(member function) 是類的重要組成部分之一。它們定義了類的行為,即對象可以執行的操作。
????????它的用法和作用和之前學習的函數基本上是一樣的,也有返回值和函數類型,它與一般函數的區別只是: 它是屬于一個類的成員,出現在類體中。
????????在使用類函數時,要注意調用它的權限 (它能否被調用 )以及它的作用域 (函數能使用什么范圍中的數據和函數 )。
成員函數定義方式
① 在類內定義(隱式內聯)
class Circle {
private:
????????double radius;
public:
????????void setRadius(double r) { radius = r; } // 類內定義
????????double getArea() { return 3.14159 * radius * radius; }
};
?? 這種方式適合簡單函數,編譯器會嘗試將其優化為內聯函數 (inline)。
?② 在類外定義(推薦)
class Circle {
private:
????????double radius;
public:
????????void setRadius(double r); // 函數聲明
????????double getArea();
};
// 類外實現
// 返回值類型 類名::函數名(參數列表);
void Circle::setRadius(double r) {
????????radius = r;
}
double Circle::getArea() {
????????return 3.14159 * radius * radius;
}
? 推薦這種方式:代碼更清晰、結構更合理,便于維護。?
注意:
如果函數名前面既無類名又無 ∷ ,如 ∷display() 或 display() ,這表示 display 函數不屬于任何類,這個函數不是成員函數,而是全局函數,即非成員函數的一般普通函數。
③ 成員函數的調用?
成員函數可以通過對象或指針來調用:
Circle c;
c.setRadius(5.0); // 通過對象調用
c.printInfo();
Circle* pc = &c;
pc->setRadius(10.0); // 通過指針調用
④ const成員函數
class Rectangle {
private:
????????int width, height;
public:
????????int area() const {
????????????????return width * height; // 不允許修改成員變量
}
????????//獲取周長
????????int perimeter() const;
};
//類外定義const函數,一定要跟上const修飾
int Rectangle::perimeter() const
{
????????//width = 2; error
????????return 2 * (width + height);
}
🔒 注意:const 成員函數只能訪問其他 const 成員函數,不能修改任何數據成員。
?7、inline函數
????????為了減少時間開銷,如果在類體中定義的成員函數中不包括循環等控制結構,C++系統會自動將它們作為內聯 inline 函數來處理。
class Circle {
private:
????????double radius;
public:
????????//類內定義,即使沒有inline聲明,默認也為inline函數
????????void setRadius(double r) { radius = r; }
????????double getArea() { return 3.14159 * radius * radius; }
};
在調用 setRadius、getArea 時,并不真正執行函數調用過程 ,而是將函數代碼嵌入程序的調用點,大大減少了調用成員函數的時間開銷。
注意1:
如果成員函數在類體外定義**,系統并不把它默認為 inline 函數。
注意2:
如果要在類體外定義 inline 函數,需要在函數定義前加上 inline ,還應將類定義和成員函數的定義都放在同一個頭文件中 (或者寫在同一個源文件中),否則編譯時無法進行代碼嵌入。例如:
?//函數定義時加inline,聲明時則不需要
inline void Circle∷setRadius(double r)
{
????????radius = r;
}
弊端:上述做法,不利于類的接口與類的實現分離,不利于信息隱蔽。雖然程序的執行效率提高了,但從軟件工程質量的角度來看,這樣做并不是好的辦法。
?8、對象內存
????????用類去定義對象時,系統會為每一個對象分配存儲空間。每個對象占用內存空間大小,只取決于數據成員和對齊方式,和成員函數沒有任何關系。
案例:?
#include <iostream>
#include <cstddef> // for alignof
using namespace std;class MyClass {
public:int x;double y;char z;void disp() {cout << "x: " << x << ", y: " << y << ", z: " << z << endl;}
};int main() {cout << "Alignment of MyClass: " << alignof(MyClass) << " bytes" << endl;cout << "Size of MyClass: " << sizeof(MyClass) << " bytes" << endl;MyClass obj;obj.x = 10;obj.y = 3.14;obj.z = 'A';obj.disp();MyClass obj2;obj2.x = 3;obj2.y = 2.5;obj2.z = 'x';obj2.disp();return 0;
}
思考:為什么調用不同對象成員函數時,執行完全相同的一段函數代碼,但是執行結果是不相同的?答:因為成員函數可以訪問和操作對象的實例數據(屬性)。每個對象的屬性值是獨立存儲的,因此即使函數代碼相同,執行時基于不同的屬性值會產生不同的結果。
?9、this指針
每個非靜態成員函數都有一個隱藏參數: this ,它是一個指向當前對象的指針。
- this 是指向調用該函數的對象的指針
- 常用于解決形參與成員變量同名的問題
- 可以用來返回當前對象(常用于鏈式調用)
案例:
#include <iostream>
#include <string> // 補充缺失的頭文件
using namespace std;class MyClass {
public:string name;int x;double y;char z;void disp() {// this指向成員函數的調用者cout << "this: " << this << endl;cout << "name: " << this->name << ", x: " << this->x;cout << ", y: " << this->y << ", z: " << this->z << endl;}void setName(const string& name) {this->name = name; // 也可以區分同名變量}
};int main() {MyClass c1 = {"zs", 10, 3.14, 'M'};cout << "&c1: " << &c1 << endl;c1.disp();cout << "--------------" << endl;MyClass c2 = {"jack", 20, 2.4, 'F'};cout << "&c2: " << &c2 << endl;c2.disp();return 0;
}
10、類與結構體的區別?
C++增加了class類型后,仍保留了結構體類型(struct), 它不是簡單地繼承 C 的結構體,而是使它也具有類的特點,以便于用于面向對象程序設計。用struct聲明的結構體類型實際上也就是類,兩者區別如下:
struct的缺省作用域為public
class的缺省作用域為private
11、封裝特性?
類背后蘊含的思想是數據抽象和封裝,兩個重要優點:
- 避免類內部出現無意的、可能破壞對象狀態的用戶級錯誤
- 隨時間推移根據需求改變或缺陷報告來完善類實現,而不需要改變用戶級代碼
????????C++通過類實現數據的封裝,將相關的數據與對數據的操作封裝到一個類中。
????????類里面的數據和成員函數通過成員限定符的修飾,改變了這些成員的訪問屬性,實現了信息的隱蔽。
????????在修改代碼時,具體修改某個類,不會對別的類產生影響,方便調試檢查錯誤,這對于大型項目尤為重要。
在C++中,封裝通常通過以下方式實現:
- 使用 private 或 protected 訪問修飾符來限制對類成員的直接訪問
- 提供公共方法( getter 和 setter )來間接訪問或修改私有成員變量
案例:
class BankAccount {
private:double balance; // 私有成員變量,外部無法直接訪問public:// 構造函數BankAccount(double initialBalance) : balance(initialBalance) {}// Getter 方法double getBalance() const {return balance;}// 存款方法void deposit(double amount) {if (amount > 0) {balance += amount;}}// 取款方法bool withdraw(double amount) {if (balance >= amount && amount > 0) {balance -= amount;return true;}return false;} // 修正:將分號移至此處
}; // 修正:類定義結束符
?????????在這個例子中, balance 變量是私有的,外界不能直接訪問它。相反,提供了 getBalance , deposit 和 withdraw 方法作為與 balance 交互的接口。