1.C++中的const
關鍵字有哪些用法?
- 1.修飾變量:表示變量的值不可修改。
-
const int a = 10;
- 2.修飾指針:
-
const int* p: // 指針指向的內容不可修改。 int* const p: // 指針本身不可修改。 const int* const p: // 指針和指針指向的內容都不可修改。
- 3.修飾成員函數:表示該函數不會修改類的成員變量。
-
void func() const;
- 4.修飾函數參數:表示函數內部不會修改該參數。
-
void func(const int& x);
2.C++中的static
關鍵字有哪些用法?
- 1.修飾局部變量:使局部變量的生命周期延長到整個程序運行期間,但作用域不變。
-
void func() { static int count = 0; count++; }
- 2.修飾全局變量或函數:限制其作用域為當前文件。
-
static int globalVar = 10;
- 3.修飾類的成員變量:使該成員變量屬于類而不是類的實例,所有實例共享同一個靜態成員變量。
-
class A { public: static int count; };
- 4.修飾類的成員函數:靜態成員函數不能訪問類的非靜態成員,只能訪問靜態成員。
-
class A { public: static void func(); };
3. C++中的虛函數和純虛函數有什么區別??
- 虛函數:在基類中使用
virtual
關鍵字聲明的函數,派生類可以重寫該函數。基類可以定義虛函數的實現。 -
class Base { public: virtual void func() { cout << "Base" << endl; } };
- 純虛函數:在基類中使用
virtual
關鍵字聲明并且沒有實現的函數,派生類必須重寫該函數。含有純虛函數的類稱為抽象類,不能實例化。 -
class Base { public: virtual void func() = 0; };
4.?C++的多態形式
????????多態(Polymorphism)定義:多態性是面向對象編程(OOP)中的一個核心概念,它允許不同類的對象通過相同的接口調用不同的方法。在C++中,多態性通常通過繼承和虛函數來實現。
實現方式:
1.基于繼承的多態(運行時多態):通過基類指針或引用來調用派生類中的重寫方法。
class Base {
public:virtual void show() {cout << "Base class show function" << endl;}
};class Derived : public Base {
public:void show() override {cout << "Derived class show function" << endl;}
};int main() {Base* basePtr;Derived derivedObj;basePtr = &derivedObj;basePtr->show(); // 調用的是Derived類的show方法return 0;
}
2.基于函數重載和模板的多態(編譯時多態)(雖然這不是傳統意義上的多態,但在某些情況下可以模擬多態行為)
注意:多態性要求基類中的函數必須是虛函數(使用virtual
關鍵字),且派生類中對該函數進行了重寫(使用override
關鍵字,C++11及以后)。
-
函數重載:允許在同一個作用域內定義多個同名函數,但這些函數的參數列表必須不同。
void print(int i) {cout << "Integer: " << i << endl; }void print(double d) {cout << "Double: " << d << endl; }
-
模板:允許編寫與類型無關的代碼,編譯器在編譯時會自動根據實參類型生成相應的函數或類。
template <typename T> void print(T t) {cout << t << endl; }
5.?C++的重載(Overloading)
定義:函數重載是指在同一作用域內,可以聲明多個具有相同名稱但參數列表不同的函數。參數列表的不同可以是參數的類型、數量或順序。
示例:
class MathUtils {
public:int add(int a, int b) {return a + b;}double add(double a, double b) {return a + b;}int add(int a, int b, int c) {return a + b + c;}
};
在這個例子中,MathUtils
類有三個名為add
的函數,但它們的參數列表不同,因此可以共存。
-
關于多態:
- 虛函數表(vtable)是如何實現的?它是如何支持多態性的?
- 答案:虛函數表是一個指向函數指針數組的指針,每個類都有一個自己的虛函數表。當通過基類指針或引用調用虛函數時,編譯器會查找該指針所指向的虛函數表,并根據對象實際的類型找到正確的函數指針進行調用。
- 虛析構函數的作用是什么?為什么需要它?
- 答案:虛析構函數確保當通過基類指針刪除派生類對象時,能夠調用到派生類的析構函數,從而正確釋放資源。如果沒有虛析構函數,可能會導致資源泄漏或未定義行為。
- 虛函數表(vtable)是如何實現的?它是如何支持多態性的?
-
關于重載:
- 函數重載是如何實現的?編譯器如何區分同名函數?
- 答案:函數重載是通過函數名稱修飾(name mangling)來實現的。編譯器會根據函數的參數列表生成一個唯一的函數標識符,從而區分同名但參數不同的函數。
- 構造函數可以被重載嗎?如果可以,有什么注意事項?
- 答案:是的,構造函數可以被重載。每個構造函數都必須有獨特的參數列表。在重載構造函數時,需要確保每個構造函數都能正確地初始化對象的狀態,并避免重復或沖突的代碼。
- 函數重載是如何實現的?編譯器如何區分同名函數?
6.?虛擬地址和物理地址?
(1)基本概念
-
地址空間:一個非負整數地址的有序集合,用于標識內存位置。
- 虛擬地址空間 (Virtual Address Space):由 CPU 生成的邏輯地址集合,大小為?N=2的n次方(如 32 位系統為?2的32次方=4GB)。
- 物理地址空間 (Physical Address Space):實際物理內存(RAM)的地址集合,大小為?MM?字節(如 8GB 物理內存對應?M=8×2的30次方字節)。
-
虛擬地址與物理地址的關系:
- 每個存儲單元(字節)在虛擬地址空間和物理地址空間中分別有一個地址。
- 虛擬地址是程序直接使用的邏輯地址,物理地址是內存硬件實際訪問的地址。
(2)虛擬內存的核心思想
????????虛擬內存允許數據對象擁有多個獨立的虛擬地址,這些地址來自不同的虛擬地址空間。例如:
- 不同進程的映射:同一物理內存頁(如共享庫)可被映射到不同進程的虛擬地址空間中。
- 同一進程的多重映射:同一虛擬地址在不同時刻可映射到不同物理地址(如動態內存分配)。
(3)地址轉換機制
????????虛擬地址到物理地址的轉換由 MMU(內存管理單元) 完成,核心步驟如下:
-
分頁機制:
- 內存被劃分為固定大小的頁(如 4KB)。
- 虛擬地址和物理地址均按頁對齊,頁內偏移直接映射。
-
頁表映射:
- 頁表 (Page Table):操作系統為每個進程維護的數據結構,記錄虛擬頁號到物理頁幀號的映射。
- 頁表項 (PTE):包含物理頁幀號、權限位(讀/寫/執行)、有效位(是否在內存中)等。
-
轉換流程:
- 虛擬地址拆分為?頁號(高位)?和?頁內偏移(低位)。
- MMU 通過頁號查詢頁表,獲取物理頁幀號。
- 物理地址 = 物理頁幀號 × 頁大小 + 頁內偏移。
示例:
- 虛擬地址?
0x0804A000
(頁號?0x0804A
,偏移?0x000
)→ 物理頁幀?0x12345
?→ 物理地址?0x12345000
。
(4)虛擬內存的優勢
- 內存隔離:每個進程擁有獨立虛擬地址空間,防止非法訪問。
- 內存擴展:通過磁盤交換(Swap)支持運行超過物理內存大小的程序。
- 共享內存:不同進程的虛擬地址可映射到同一物理頁(如動態庫、進程間通信)。
- 簡化編程:程序無需關心物理內存分配細節(如碎片、地址沖突)。
關鍵問題與場景?
Q1:虛擬地址空間可以大于物理地址空間嗎?
- 可以。例如,32 位系統虛擬地址空間為 4GB,物理內存可能僅 2GB,剩余部分通過磁盤交換模擬。
Q2:虛擬地址是否一定對應物理地址?
- 不一定。虛擬地址可能:
- 未分配(如未使用的堆空間)。
- 已分配但未加載到內存(觸發缺頁中斷)。
- 映射到文件(內存映射文件,
mmap
)。
Q3:如何實現多個虛擬地址映射到同一物理地址?
- 共享內存:多個進程的頁表項指向同一物理頁幀。
- 示例:動態庫代碼段在多個進程間共享,減少內存冗余。
7.?Linux字符設備
1. 什么是字符設備?
- 定義:字符設備(Character Device)是以**字節流(Byte Stream)**形式進行數據讀寫的設備,數據按順序訪問,不支持隨機尋址。
- 典型例子:鍵盤、鼠標、串口、終端(
/dev/tty
)、聲卡等。 - 與塊設備的區別:
- 訪問方式:字符設備按字節流操作,塊設備按固定大小的數據塊(如 512B)操作。
- 緩存機制:塊設備通常有內核緩存(Buffer Cache),而字符設備無緩存(直接讀寫硬件)。
- 使用場景:字符設備用于實時性高或數據量小的設備,塊設備用于存儲設備(如硬盤)。
2. 字符設備驅動核心機制
(1) 設備號(Major & Minor Number)
- 主設備號(Major Number):標識設備類型,同一類設備共享同一主設備號。
- 次設備號(Minor Number):區分同一類型設備的不同實例(如多個串口)。
- 注冊方法:
-
// 靜態分配(需確保未被占用) int register_chrdev_region(dev_t first, unsigned int count, const char *name);// 動態分配(推薦) int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, const char *name);
(2) 設備文件
- 設備節點:用戶通過文件(如?
/dev/mydev
)訪問設備。 - 創建方式:
- 手動創建:
mknod /dev/mydev c 250 0
(主設備號250,次設備號0)。 - 自動創建:通過?
udev
?或?devtmpfs
?根據?sysfs
?信息自動生成。
- 手動創建:
(3) 驅動核心結構體:file_operations
- 作用:定義設備操作函數(如?
open
,?read
,?write
),用戶調用系統調用時觸發。 - 關鍵成員函數:
-
struct file_operations {struct module *owner;int (*open)(struct inode *, struct file *);ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long);int (*release)(struct inode *, struct file *);// 其他函數:poll, mmap, fsync 等 };
(4) 驅動注冊流程
- 分配設備號:
alloc_chrdev_region
。 - 初始化?
cdev
:struct cdev my_cdev; cdev_init(&my_cdev, &my_fops); // 綁定 file_operations
- 添加設備到內核:
cdev_add(&my_cdev, dev, count)
。 - 創建設備節點(可選自動方式):
struct class *my_class = class_create(THIS_MODULE, "my_class"); device_create(my_class, NULL, dev, NULL, "mydev");
(5) 相關問題:
Q1: 字符設備和塊設備有什么區別?
- 回答要點:
- 字符設備按字節流訪問,塊設備按數據塊訪問。
- 字符設備無內核緩存,塊設備有緩存機制。
- 典型應用場景:字符設備用于交互式設備(如串口),塊設備用于存儲(如硬盤)。
Q2: 如何注冊一個字符設備驅動?
- 回答示例:
- 使用?
alloc_chrdev_region
?動態分配設備號。 - 定義并填充?
file_operations
?結構體。 - 初始化?
cdev
?并通過?cdev_add
?注冊到內核。 - 使用?
class_create
?和?device_create
?自動生成設備節點。
- 使用?
Q3:?file_operations
?中的?read
?和?write
?函數如何實現?
- 回答示例:
read
:從設備讀取數據到用戶空間緩沖區,使用?copy_to_user
。-
static ssize_t my_read(struct file *filp, char __user *buf, size_t len, loff_t *off) {char kernel_buf[256];// 從硬件讀取數據到 kernel_bufcopy_to_user(buf, kernel_buf, min(len, sizeof(kernel_buf)));return len; }
write
:從用戶空間緩沖區寫入設備,使用?copy_from_user
。-
static ssize_t my_write(struct file *filp, const char __user *buf, size_t len, loff_t *off) {char kernel_buf[256];copy_from_user(kernel_buf, buf, min(len, sizeof(kernel_buf)));// 將 kernel_buf 寫入硬件return len; }
Q4: 用戶程序如何與字符設備交互?
- 回答要點:
- 用戶程序通過系統調用(如?
open
,?read
,?write
,?ioctl
)操作設備文件。 - 示例代碼:
-
int fd = open("/dev/mydev", O_RDWR); read(fd, buf, sizeof(buf)); write(fd, buf, sizeof(buf)); ioctl(fd, MY_CMD, arg); close(fd);
- 用戶程序通過系統調用(如?
8.?進程,線程,協程區別,進程間通信、怎樣做同步、信號量和鎖
(1)基本概念對比
特性 | 進程 | 線程 | 協程 |
---|---|---|---|
定義 | 操作系統資源分配的基本單位,獨立地址空間。 | 進程內的執行單元,共享進程資源。 | 用戶態輕量級線程,由程序控制調度。 |
資源開銷 | 高(獨立內存、文件句柄等)。 | 低(共享進程資源)。 | 極低(僅需保存寄存器狀態和棧)。 |
切換成本 | 高(需切換地址空間、內核態操作)。 | 中(需內核調度)。 | 低(用戶態切換,無內核介入)。 |
并發性 | 多進程并行(依賴多核)。 | 多線程并行(共享進程資源)。 | 單線程內高并發(異步I/O或協作式調度)。 |
通信方式 | IPC(管道、共享內存等)。 | 共享內存(需同步)。 | 直接傳遞數據(如通道、隊列)。 |
典型應用 | 隔離性任務(如瀏覽器多標簽頁)。 | 高并發I/O操作(如Web服務器)。 | 高并發I/O密集型任務(如爬蟲、微服務)。 |
?(2)進程間通信(IPC)方式
【GL011】操作系統-CSDN博客文章瀏覽閱讀975次,點贊21次,收藏19次。本篇文章是關于操作系統的知識混淆點,建議經常食用!https://blog.csdn.net/qq_68192341/article/details/144378829?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522efe8ef7a25b42058cc3baedf821c4263%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=efe8ef7a25b42058cc3baedf821c4263&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-144378829-null-null.nonecase&utm_term=%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1&spm=1018.2226.3001.4450
9. C++設計模式
1. 單例模式(Singleton)
- 作用:確保一個類只有一個實例,并提供全局訪問點。
- 場景:配置管理、日志系統、數據庫連接池。
- 實現(C++11?線程安全):
-
class Singleton { public:static Singleton& getInstance() {static Singleton instance; // C++11 保證線程安全return instance;}void doSomething() { /* ... */ } private:Singleton() = default; // 私有構造函數Singleton(const Singleton&) = delete; // 禁止拷貝Singleton& operator=(const Singleton&) = delete; // 禁止賦值 };
-
2. 工廠模式(Factory Method)
- 作用:將對象創建延遲到子類,通過接口統一創建過程。
- 場景:跨平臺 UI 組件、數據庫驅動。
- 示例:
-
class Product { public:virtual void use() = 0; }; class ConcreteProduct : public Product {void use() override { std::cout << "Using product\n"; } }; class Factory { public:virtual Product* createProduct() = 0; }; class ConcreteFactory : public Factory {Product* createProduct() override { return new ConcreteProduct(); } };
3. 抽象工廠模式(Abstract Factory)
- 作用:創建一組相關或依賴對象的家族,無需指定具體類。
- 場景:跨主題 GUI 庫(如不同風格的按鈕和文本框)。
- 示例:
-
class Button { /* ... */ }; class WinButton : public Button { /* ... */ }; class MacButton : public Button { /* ... */ };class GUIFactory { public:virtual Button* createButton() = 0; }; class WinFactory : public GUIFactory {Button* createButton() override { return new WinButton(); } }; class MacFactory : public GUIFactory {Button* createButton() override { return new MacButton(); } };
4. 建造者模式(Builder)
- 作用:分步驟構造復雜對象,分離構建與表示。
- 場景:構造 HTTP 請求、游戲角色生成。
- 示例:
-
class Car {std::string engine, wheels; public:void setEngine(const std::string& e) { engine = e; }void setWheels(const std::string& w) { wheels = w; } };class CarBuilder { public:virtual void buildEngine() = 0;virtual void buildWheels() = 0;virtual Car getResult() = 0; }; class SportsCarBuilder : public CarBuilder {Car car;void buildEngine() override { car.setEngine("V8"); }void buildWheels() override { car.setWheels("Racing"); }Car getResult() override { return car; } };
................................很多................................
模式類別 | 典型模式 | 核心思想 |
---|---|---|
創建型 | 工廠、單例、建造者 | 解耦對象創建與使用 |
結構型 | 適配器、裝飾器、代理 | 組合對象形成更大結構 |
行為型 | 觀察者、策略、狀態 | 優化對象間通信與職責分配 |
?
10.?觀察者模式如何用C++實現
觀察者模式(Observer)
- 作用:定義對象間的一對多依賴,當對象狀態變化時自動通知依賴項。
- 場景:事件處理系統、MVC 模型更新。