C++面向對象的常見面試題目(一)

1. 面向對象的三大特征

(1)封裝:隱藏對象的內部狀態,只暴露必要的接口。

#include <iostream>
#include <string>// 定義一個簡單的類 Person
class Person {
private: // 私有成員,外部不可直接訪問std::string name;int age;public: // 公共方法,外部可以訪問// 構造函數,用于初始化對象Person(std::string n, int a) {name = n;age = a;}// 公共方法,用于設置年齡void setAge(int a) {if (a > 0 && a < 150) { // 簡單的年齡驗證age = a;} else {std::cout << "Invalid age!" << std::endl;}}// 公共方法,用于獲取姓名std::string getName() {return name;}// 公共方法,用于獲取年齡int getAge() {return age;}
};int main() {// 創建 Person 對象Person p1("Alice", 30);// 嘗試直接訪問私有成員(編譯錯誤)// std::cout << p1.name << std::endl;// 使用公共方法設置年齡p1.setAge(35);// 使用公共方法獲取姓名和年齡std::cout << "Name: " << p1.getName() << ", Age: " << p1.getAge() << std::endl;return 0;
}

(2)繼承:無需修改原有類的情況下對功能實現拓展

#include <iostream>
#include <string>// 定義一個基類 Animal
class Animal {
protected: // 受保護的成員,子類可以訪問std::string name;public:// 構造函數,初始化 Animal 的名稱Animal(std::string n) : name(n) {}// 公共方法,用于輸出 Animal 的聲音void makeSound() const {std::cout << "Animal " << name << " makes a sound" << std::endl;}
};// 定義一個派生類 Dog,繼承自 Animal
class Dog : public Animal {
private:std::string breed;public:// 構造函數,初始化 Dog 的名稱和品種Dog(std::string n, std::string b) : Animal(n), breed(b) {}// 重寫父類的 makeSound 方法void makeSound() const {std::cout << "Dog " << name << " barks" << std::endl;}// 新的方法,用于輸出 Dog 的品種void showBreed() const {std::cout << "Dog " << name << " is of breed " << breed << std::endl;}
};int main() {// 創建 Animal 對象Animal a("Generic");// 調用 Animal 的方法a.makeSound();// 創建 Dog 對象Dog d("Buddy", "Labrador");// 調用 Dog 的方法d.makeSound();d.showBreed();// 使用基類指針指向派生類對象,演示多態Animal* animalPtr = &d;animalPtr->makeSound(); // 調用的是 Dog 的 makeSound 方法return 0;
}

(3)多態:同一個函數名在不同對象中有不同的行為。通過時下接口重用增強可拓展性

多態分為2類,靜態多態和動態多態。

靜態多態:又稱編譯時多態。通過函數重載和運算符重載實現。

動態多態:又稱運行時多態,通過虛函數和繼承實現。

虛函數:在基類中聲明函數為虛函數,派生類可以覆蓋這些虛函數,從而實現不同的行為。在運行時,根據對象的實際類型決定調用哪個版本的函數,這種機制稱為動態綁定或后期綁定。

// 靜態多態
#include <iostream>class MathTool {
public:// 計算整數平方int calculateSquare(int number) {return number * number;}// 計算浮點數平方double calculateSquare(double number) {return number * number;}
};int main() {MathTool tool;// 靜態多態示例:根據參數類型自動選擇合適的方法std::cout << "整數5的平方是: " << tool.calculateSquare(5) << std::endl;      // 調用整數版本std::cout << "浮點數2.5的平方是: " << tool.calculateSquare(2.5) << std::endl; // 調用浮點數版本return 0;
}// 動態多態
#include <iostream>// 基類,含有虛函數
class Animal {
public:virtual ~Animal() {} // 虛析構函數,確保通過基類指針刪除派生類對象時能正確調用派生類的析構函數virtual void makeSound() {std::cout << "Some animal makes a sound." << std::endl;}
};// 派生類1
class Dog : public Animal {
public:void makeSound() override {std::cout << "Dog barks." << std::endl;}
};// 派生類2
class Cat : public Animal {
public:void makeSound() override {std::cout << "Cat meows." << std::endl;}
};int main() {Animal* animalPtr; // 基類指針// 動態分配內存給派生類對象animalPtr = new Dog();animalPtr->makeSound(); // 運行時動態決定調用Dog的makeSound()animalPtr = new Cat();animalPtr->makeSound(); // 運行時動態決定調用Cat的makeSound()delete animalPtr; // 由于基類析構函數為虛函數,可以安全刪除派生類對象return 0;
}

2. 多態的實現原理

(1)靜態多態

原理:函數名修飾。編輯器會根據函數的簽名(包括參數類型、數量和順序)對內部的函數名稱進行編碼,生成一個唯一的標識符。這個編碼后的名稱包含了足夠的信息,使得編譯器能夠準確地區分不同的重載版本,即使它們的外部名稱相同。

編譯過程:

????????預編譯:把頭文件中的函數聲明拷貝到源文件,避免編譯過程中語法分析找不到函數定義。

? ? ? ? 編譯:語法分析,同時進行符號匯總

? ? ? ? 匯編:生成函數名到函數地址的映射,方便之后通過函數名照到函數定義從而執行函數。

? ? ? ? 鏈接:講過個文件的符號表匯總合并

早綁定:編譯器編譯時就已經確定對象調用的函數的地址。靜態多態依賴于早綁定。

(2)動態多態

原理:虛函數重寫。當一個類中聲明了至少一個虛函數時,編譯器會為該類生成一個虛函數表。這是一個存儲虛函數指針的數組,每個指針指向類中相應虛函數的實現。含有虛函數的類實例在內存中除了包含數據成員外,還會有一個指向其所屬類的虛函數表的指針(通常稱為vptr)。這個vptr是在對象創建時由編譯器自動初始化的。在派生類中,當你定義了一個與基類中虛函數同名且簽名相同的函數時,這就是虛函數的重寫。派生類的虛函數表中,對應的條目會存儲派生類中該函數的地址,而非基類的。

虛函數重寫:基類函數上加上virtual關鍵字,在派生類重寫虛函數。運行時會根據對象的類型調用相應的函數。如果對象的類型是基類,那么調用基類的函數,如果對象的類型是派生類誒,則調用派生類的函數

晚綁定:程序運行時才確定對象調用的函數的地址。動態多態依賴于晚綁定。C++中,晚綁定是通過virtual關鍵字實現的。

3. 怎么解決菱形繼承

因為c++有多重繼承的特性,導致一個子類可能繼承多個基類。這些基類可能繼承自相同的基類,從而造成了菱形繼承。

菱形繼承的問題主要是數據冗余并且造成二義性。

二義性:當一個派生類(我們稱之為D)從兩個不同的基類(比如B1B2)繼承,而這兩個基類又都繼承自同一個基類(A),那么在D中直接訪問從A繼承來的成員時,編譯器無法確定應該使用B1繼承的版本還是B2繼承的版本。這種情況下,編譯器會報錯,指出存在二義性。

數據冗余:如果不使用虛擬繼承(virtual關鍵字),每個派生類(B1B2)都會包含基類A的一個完整副本。因此,當D繼承自B1B2時,它將擁有兩份A的成員,導致數據冗余。這不僅浪費存儲空間,還可能導致邏輯上的混亂,尤其是在修改這些成員時,可能會忘記更新所有副本,從而引發一致性問題。

使用虛繼承可以解決菱形繼承問題。在派生類對共同基類進行虛繼承時,編譯器會確保只有一份共同基類的實例,而不是每個中間類都有自己的一份。

4. 關鍵字override、final的作用

子類繼承基類的虛函數之后,可能存在這樣的問題:子類不希望這個函數會自己的子類被進一步重寫;子類想重寫一個新函數,但是錯誤的重寫了基類虛函數;子類本意是重寫基類的虛函數但是簽名不一致導致重新構建了一個虛函數;子類希望自己絕后!但是沒有辦法從語法上完成這件事。

override可以指定子類的一個虛函數復寫基類的一個虛函數;并且保證該重寫的虛函數與子類的虛函數有相同的簽名。

final可以指定某個虛函數不能在派生類中覆蓋,或者某個類不能被派生。加在類前面就可以阻塞類的進一步派生。

5. C++類型推導的用法

C++類型推導主要有三個方面:auto、decltype、模板類型推導

(1)auto:用于推導變量的類型,通過強制聲明一個變量的初始值,編譯器會通過初始值進行推導類型

(2)?decltype

用于推導表達式的類型,編譯器只分析表達式類型而不參與運算

?6. C++11 中function、lambda、bind之間的關系

std::function是一個抽象了函數參數和函數返回值的類模板。它把任意函數包裝成了一個對象,該對象可以保存傳遞以及復制。也可以進行動態綁定,只需要修改該對象,實現類似多態的效果

#include <iostream>
#include <functional>// 函數對象(仿函數)
struct Adder {int operator()(int a, int b) const {return a + b;}
};// 普通函數
int subtract(int a, int b) {return a - b;
}// 成員函數
struct Calculator {int multiply(int a, int b) {return a * b;}
};int main() {// 使用 std::function 聲明不同類型的可調用對象std::function<int(int, int)> func1 = Adder();  // 函數對象std::function<int(int, int)> func2 = subtract; // 普通函數Calculator calc;std::function<int(Calculator&, int, int)> func3 = &Calculator::multiply;  // 成員函數// 調用并輸出結果std::cout << "Adder result: " << func1(10, 5) << std::endl;std::cout << "Subtract result: " << func2(10, 5) << std::endl;std::cout << "Multiply result: " << func3(calc, 10, 5) << std::endl;return 0;
}

lambda表達式:一種方面的創建匿名函數的語法糖

原理:編譯的時候把表達式轉變為一個函數對象,然后根據表達式慘呼列表重載operate ()

demo:

// 簡單表達式
auto sayHello = []{ std::cout << "Hello, World!\n"; };
sayHello();// 捕獲外部變量
int x = 10, y = 20;
auto incrementAndPrint = [x, &y]() {x++; // 按值捕獲,不會改變外部xy++; // 按引用捕獲,會改變外部ystd::cout << "x: " << x << ", y: " << y << "\n";
};
incrementAndPrint(); // 輸出:x: 11, y: 21
std::cout << "After: x=" << x << ", y=" << y << "\n"; // 輸出:x=10, y=21// 指定返回類型
int x = 10, y = 20;
auto incrementAndPrint = [x, &y]() {x++; // 按值捕獲,不會改變外部xy++; // 按引用捕獲,會改變外部ystd::cout << "x: " << x << ", y: " << y << "\n";
};
incrementAndPrint(); // 輸出:x: 11, y: 21
std::cout << "After: x=" << x << ", y=" << y << "\n"; // 輸出:x=10, y=21

std::bind:用來通過綁定函數以及函數參數的方式生成函數對象的模板函數,提供占位符,實現靈活的參數綁定

#include <iostream>
#include <functional>// 通用加法函數
int add(int x, int y) {return x + y;
}int main() {// 使用std::bind固定add的第一個參數為5auto addFive = std::bind(add, 5, std::placeholders::_1);// 現在addFive是一個新的可調用對象,只需要一個參數std::cout << "Adding 5 to 3 gives: " << addFive(3) << '\n'; // 輸出: 8std::cout << "Adding 5 to 10 gives: " << addFive(10) << '\n'; // 輸出: 15return 0;
}

總結:function 用來描述函數對象的類型;lambda 表達式用來生成函數對象(可以訪問外部變量的匿名函數);bind 也是用來生成函數對象(函數和參數進行綁定生成函數對象);

這是一條吃飯博客,由挨踢零聲贊助。學C/C++就找挨踢零聲,加入挨踢零聲,面試不挨踢!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/42487.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/42487.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/42487.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Mac OS ssh 連接提示 Permission denied (publickey)

這錯誤有點奇葩&#xff0c;MacBook的IDE(vscode和pycharm)遠程都連不上&#xff0c;terminal能連上&#xff0c;windows的pycharm能連上&#xff0c;見鬼了&#xff0c;所以肯定不是秘鑰的問題了&#xff0c;查了好久竟然發現是權限的問題。。 chmod 400 ~/.ssh/id_rsa http…

華為機試HJ37統計每個月兔子的總數

華為機試HJ37統計每個月兔子的總數 題目&#xff1a; 想法&#xff1a; 上述題目實際是一個斐波那契數列&#xff0c;利用斐波那契數列對問題進行求解 input_number int(input())def fib(n):if n < 2:return 1else:n_1 1n_2 1count 2while count < n:n_1, n_2 n_…

【Android】【多屏】多屏異顯異觸調試技巧總結

這里寫目錄標題 如何獲取多屏IDs獲取多屏的size/density如何啟動應用到指定DisplayId多屏截屏/錄屏screencapscreenrecord發送按鍵到指定DisplayId 如何獲取多屏IDs dumpsys display | grep mDisplayIdtrinket:/ # dumpsys display | grep mDisplayIdmDisplayId0mDisplayId2 t…

【AI資訊】可以媲美GPT-SoVITS的低顯存開源文本轉語音模型Fish Speech

Fish Speech是一款由fishaudio開發的全新文本轉語音工具&#xff0c;支持中英日三種語言&#xff0c;語音處理接近人類水平&#xff0c;使用Flash-Attn算法處理大規模數據&#xff0c;提供高效、準確、穩定的TTS體驗。 Fish Audio

區塊鏈技術的應用場景和優勢。

區塊鏈技術具有廣泛的應用場景和優勢。 區塊鏈技術的應用場景&#xff1a; 1. 金融服務&#xff1a;區塊鏈可用于支付、跨境匯款、借貸和結算等金融服務&#xff0c;提高交易效率、降低成本并增強安全性。 2. 物聯網&#xff08;IoT&#xff09;&#xff1a;區塊鏈可以用于物…

機器學習Day12:特征選擇與稀疏學習

1.子集搜索與評價 相關特征&#xff1a;對當前學習任務有用的特征 無關特征&#xff1a;對當前學習任務沒用的特征 特征選擇&#xff1a;從給定的特征集合中選擇出相關特征子集的過程 為什么要特征選擇&#xff1f; 1.任務中經常碰到維數災難 2.去除不相關的特征能降低學習的…

Git注釋規范

主打一個有用 代碼的提交規范參考如下&#xff1a; init:初始化項目feat:新功能&#xff08;feature&#xff09;fix:修補bugdocs:文檔&#xff08;documentation&#xff09;style:格式&#xff08;不影響代碼運行的變動&#xff09;refactor:重構&#xff08;即不是新增功能…

NodeJs獲取文件擴展名

path.extname 是 Node.js 路徑模塊 (path) 中的一個方法&#xff0c;用于獲取文件路徑的擴展名。擴展名是指文件名中最后一個 .&#xff08;點&#xff09;之后的部分&#xff0c;包括這個 .。 const path require(path);const filename example.txt; const ext path.extna…

計算機網絡之令牌環

1.令牌環工作原理 令牌環&#xff08;Token Ring&#xff09;是一種局域網&#xff08;LAN&#xff09;的通信協議&#xff0c;最初由IBM在1984年開發并標準化為IEEE 802.5標準。在令牌環網絡中&#xff0c;所有的計算機或工作站被連接成一個邏輯或物理的環形拓撲結構。網絡中…

排序(2)

我們在排序&#xff08;1&#xff09;中說到選擇排序的代碼&#xff1a; void SelectSort(int* a,int n) {int begin0,endn-1;int minibegin,maxbegin;for(int ibegin1;i<end;i){if(a[i]>a[max]){maxii;}if(a[i]<a[mini]){minii;}begin;--end;}Swap(&a[beign],&a…

SKF軸承故障頻率查詢

1&#xff0c;第一步&#xff1a;搜索軸承型號 skf官網 2&#xff0c;第二步&#xff1a;查詢故障頻率。 第三步&#xff1a;

尚品匯-(十四)

&#xff08;1&#xff09;提交git 商品后臺管理到此已經完成&#xff0c;我們可以把項目提交到公共的環境&#xff0c;原來使用svn&#xff0c;現在使用git 首先在本地創建ssh key&#xff1b; 命令&#xff1a;ssh-keygen -t rsa -C "your_emailyouremail.com" I…

完美解決ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: NO)

已解決ERROR 1045 (28000): Access denied for user ‘root‘‘localhost‘ (using password: NO) 下滑查看解決方法 文章目錄 報錯問題解決思路解決方法交流 報錯問題 ERROR 1045 (28000): Access denied for user ‘root‘‘localhost‘ (using password: NO) 解決思路 對…

InfluxDB v2.x中的Flux基本概念

InfluxDB v2.x中的Flux查詢語言的核心概念主要包括以下幾個方面&#xff1a; 1. 表&#xff08;Tables&#xff09; Flux以表&#xff08;Tables&#xff09;的形式處理數據。每個表包含多行數據&#xff0c;每行數據都是一個record&#xff08;記錄&#xff09;&#xff0c;…

落日余暉映晚霞

落日余暉映晚霞&#xff0c;立于海濱&#xff0c;望夕陽余暉灑于波光粼粼之上&#xff0c;金光跳躍&#xff0c;若繁星閃爍&#xff0c;耀人心目。 海風輕拂&#xff0c;心境寧靜&#xff0c;凡塵俗務皆于此剎那消散&#xff0c;思緒萬干&#xff0c;或憶往昔點滴&#xff0c;或…

刷爆leetcode第十期

題目一 相同的樹 給你兩棵二叉樹的根節點 p 和 q &#xff0c;編寫一個函數來檢驗這兩棵樹是否相同。 如果兩個樹在結構上相同&#xff0c;并且節點具有相同的值&#xff0c;則認為它們是相同的。 首先我們要來判斷下它們的根是否相等 根相等的話是否它們的左子樹相等 是否…

在CMD中創建虛擬環境并在VSCode中使用和管理

1. 使用Conda創建虛擬環境 在CMD或Anaconda Prompt中執行以下代碼以創建一個新的虛擬環境&#xff1a; conda create -n my_env python 3.8 這樣會創建一個名為 my_env 的環境&#xff0c;并在Anaconda環境目錄下生成一個相應的文件夾&#xff0c;包含該虛擬環境所需的所有…

GD32實戰篇-雙向數控BUCK-BOOST-BOOST升壓理論基礎

本文章基于兆易創新GD32 MCU所提供的2.2.4版本庫函數開發 向上代碼兼容GD32F450ZGT6中使用 后續項目主要在下面該專欄中發布&#xff1a; https://blog.csdn.net/qq_62316532/category_12608431.html?spm1001.2014.3001.5482 感興趣的點個關注收藏一下吧! 電機驅動開發可以跳轉…

MySQL之備份與恢復(八)

備份與恢復 還原邏輯備份 如果還原的是邏輯備份而不是物理備份&#xff0c;則與使用操作系統簡單地復制文件到適當位置的方式不同&#xff0c;需要使用MySQL服務器本身來加載數據到表中。在加載導出文件之前&#xff0c;應該先花一點時間考慮文件有多大&#xff0c;需要多久加…

金蝶云蒼穹-插件開發(二)新建、更新、刪除數據

加載本頁面數據 關于加載數據&#xff0c;還要多補充一個點&#xff0c;如果要加載一個基礎資料/單據界面中正在操作的界面&#xff0c;比如要獲取剛填寫好的字段值&#xff0c;就要獲取當前界面的模型層&#xff0c;再獲取具體數據。具體操作如下&#xff1a; //獲取日任務信…