[C++面試] new、delete相關面試點

一、入門

1、說說new與malloc的基本用途

int* p1 = (int*)malloc(sizeof(int));  // C風格
int* p2 = new int(10);               // C++風格,初始化為10

new?是 C++ 中的運算符,用于在上動態分配內存調用對象的構造函數,會自動計算所需內存大小

#include <iostream>
int main() {int* ptr = new int(5);std::cout << *ptr << std::endl;delete ptr;return 0;
}

?malloc?是 C 語言中的標準庫函數,用于在上分配指定大小的內存塊,不會調用對象的構造函數,返回的是?void*?類型的指針,需要手動進行類型轉換。

int main() {int* ptr = (int*)malloc(sizeof(int));*ptr = 5;std::cout << *ptr << std::endl;free(ptr);return 0;
}

內存分配失敗處理malloc返回NULLnew拋出std::bad_alloc異常?

2、delete?和?free?分別用于什么場景?

delete?是 C++ 中的運算符,用于釋放由?new?分配的內存,并調用對象的析構函數

#include <iostream>
class MyClass {
public:~MyClass() {std::cout << "Destructor called" << std::endl;}
};
int main() {MyClass* obj = new MyClass();delete obj;return 0;
}

free?是 C 語言中的標準庫函數,用于釋放由?malloccalloc?或?realloc?分配的內存,不會調用對象的析構函數。?

#include <iostream>
#include <cstdlib>
int main() {int* ptr = (int*)malloc(sizeof(int));free(ptr);return 0;
}

3、new與malloc的關聯

new通過調用operator new分配內存,而默認的operator new內部使用malloc

void* operator new(size_t size) {  void* p = malloc(size);  if (!p) throw std::bad_alloc();  return p;  
}  

4、delete NULL或nullptr會發生什么?

?操作??空指針(NULL/nullptr)???非空指針?
delete?/?delete[]安全(無操作)需確保指針有效且未被重復釋放
free安全(無操作)需確保內存由?malloc?分配

?底層邏輯?:編譯器會檢查指針是否為空,若為空則直接跳過析構和內存釋放步驟?

最佳實踐?:釋放后立即置空指針

delete、free并不會把指針置空。

int* p = new int(10);
delete p;  // 第一次釋放
delete p;  // 危險!重復釋放非空指針

安全性:避免程序員在調用?delete?或?free?前必須顯式檢查指針是否為空(避免冗余檢查)。?

二、進階

1、new[]?和?delete[]?的作用是什么?

new[]?用于在堆上動態分配數組內存,并對數組中的每個元素調用構造函數。delete[]?用于釋放由?new[]?分配的數組內存,并對數組中的每個元素調用析構函數

include <iostream>
class MyClass {
public:MyClass() {std::cout << "Constructor called" << std::endl;}~MyClass() {std::cout << "Destructor called" << std::endl;}
};
int main() {MyClass* arr = new MyClass[3];delete[] arr;return 0;
}

C++ 編譯器在解析代碼時會忽略?delete?和?[]?之間的所有空白符?(包括空格、換行符、制表符等),推薦?delete[]?的緊湊寫法

delete[]arr;
delete []arr;
delete [] arr;
delete[] arr;

2、對于內置數據類型,使用delete、delete[]效果是一樣的。這句話對嗎?為什么?

內置類型無析構函數?:C++ 的內置數據類型(如?intlong、指針等)沒有析構函數。delete?和?delete[]?的核心差異在于是否調用析構函數,而內置類型無需析構,但內存釋放的完整性仍取決于運行時環境。某些編譯器(如 MSVC)可能通過內存池機制自動回收整個數組內存,但這屬于未定義行為,不可依賴。

int* p1 = new int(10);
delete p1;      // 正確釋放單個對象
int* p2 = new int[10];
delete[] p2;    // 正確釋放數組
delete p2;      // 未定義行為

分配時的元數據記錄?:無論是?new?還是?new[],內存分配時系統會記錄分配的內存大小和對象數量(存儲在?_CrtMemBlockHeader?等結構中)。釋放時,delete?和?delete[]?均能通過指針獲取這些信息,從而正確釋放連續內存塊?。

int* p = new int[1000];
delete p;  // 看似正常,但 Valgrind 報告 3996 字節泄漏(1000 * 4 - 4)
+-------------------+
| 數組長度(1000)  |  ← 元信息(通常占用 4/8 字節)
+-------------------+
| 元素0(int)      |  ← 用戶可見的指針 `p` 指向此處
+-------------------+
| 元素1(int)      |
+-------------------+
| ...(共 1000 個) |
+-------------------+

3、使用?new?分配內存,用?free?釋放會怎么樣??

如果使用?new?分配內存,卻用?free?釋放,對象的析構函數不會被調用,可能會導致資源泄漏,例如對象中包含動態分配的資源(如文件句柄、網絡連接等)無法正確釋放。

#include <iostream>
class MyClass {
public:~MyClass() {std::cout << "Destructor called" << std::endl;}
};
int main() {MyClass* obj = new MyClass();free(obj); // 析構函數不會被調用return 0;
}

4、?使用?malloc?分配內存,用?delete?釋放會怎么樣?

delete?會嘗試調用對象的析構函數。?malloc?分配的內存沒有經過構造函數初始化,調用析構函數可能會導致未定義行為。

#include <iostream>
#include <cstdlib>class ResourceHolder {
public:ResourceHolder() {std::cout << "ResourceHolder: Acquiring resource..." << std::endl;// 模擬資源獲取,例如打開文件、分配內存等resource = new int[100];}~ResourceHolder() {std::cout << "ResourceHolder: Releasing resource..." << std::endl;// 模擬資源釋放,例如關閉文件、釋放內存等delete[] resource;}private:int* resource;
};int main() {// 使用 malloc 分配內存ResourceHolder* holder = (ResourceHolder*)malloc(sizeof(ResourceHolder));if (holder == nullptr) {std::cerr << "Memory allocation failed!" << std::endl;return 1;}// 嘗試使用 delete 釋放內存delete holder;return 0;
}
  • 運用?malloc?為?ResourceHolder?對象分配內存。malloc?只是單純地分配指定大小的內存塊,不會調用對象的構造函數,所以?resource?指針不會被正確初始化。
  • 嘗試使用?delete?釋放內存。delete?會調用對象的析構函數,但是由于?resource?指針未被正確初始化,在析構函數中調用?delete[] resource?就會引發未定義行為,可能會導致程序崩潰或者出現其他不可預測的問題。
    • 當?delete[] resource?嘗試釋放一個未正確初始化的指針時,可能會訪問非法內存地址,從而致使程序崩潰。

5、malloc?+?delete混用問題

注:newdelete必須成對使用

  • 內存生命周期管理newdelete通過構造函數/析構函數保證對象完整生命周期
  • ?混用風險:未調用析構函數(若對象有資源需釋放)
class MyClass {int* data;  // 未初始化
public:MyClass() { data = new int[100]; }  // 構造函數未執行!~MyClass() { delete[] data; }       // 析構函數嘗試釋放野指針
};MyClass* p = (MyClass*)malloc(sizeof(MyClass));
delete p;  // 析構函數調用delete[] data,但data未初始化 → 崩潰

delete確實會調用析構函數,但這一行為是否能正確執行,取決于對象是否被正確構造。通過malloc分配內存時,MyClass的構造函數未被調用,但delete p卻嘗試調用析構函數。若析構函數中存在對未初始化成員的操作(如delete data),會導致未定義行為?(如訪問野指針,引發崩潰)

內置類型(如int)?
無構造函數和析構函數,因此malloc+delete可能不會崩潰(因為沒有析構操作),但仍是未定義行為

int* p = (int*)malloc(sizeof(int));
delete p;  // 可能不崩潰,但不符合規范

6、newfree混用

未調用析構函數,且可能因內存布局差異導致崩潰(如new[]的頭部信息未處理)

當通過new[]分配數組時,內存布局可能包含頭部信息?(記錄數組長度),例如:

MyClass* arr = new MyClass[5];
// 內存布局:[長度=5][對象1][對象2]...[對象5]

delete[]會根據頭部信息調用5次析構函數,再釋放完整內存塊。若用free釋放,?頭部信息未被處理。free?的輸入是?arr(指向第一個對象),但?new[]?分配的實際內存塊起始地址是?arr - sizeof(頭部)。

正確行為:若用戶調用?delete[] arr,會從頭部地址釋放完整內存塊(包括頭部和所有對象)。?錯誤行為:若調用?free(arr)free?僅嘗試釋放從?arr?開始的地址,而實際分配的內存塊起始位置未被正確識別,導致部分內存未被釋放?(內存泄漏)或堆結構破壞?(可能崩潰)

free(arr)?無法識別?new[]?的內存布局,會釋放不完整的地址范圍,導致內存泄漏(頭部部分對象未被釋放),甚至因堆管理器元數據損壞而崩潰。

注:不是只釋放了[長度=5][對象1][對象2]...[對象5]

free只能釋放通過malloc/calloc/realloc分配的內存塊,其底層通過內存塊的頭部元數據?(如大小信息)來釋放整個內存塊。

new[]分配的頭部信息可能格式與malloc不同,導致free無法正確解析,最終釋放的地址范圍是未定義的

  • ?可能釋放不完整free(arr)可能僅釋放從對象0地址開始的部分內存(如對象0的存儲空間),而頭部和其他對象的內存未被釋放
  • ?可能破壞堆結構:錯誤釋放地址會導致堆管理器元數據損壞,引發后續內存操作崩潰

若編譯器未添加頭部信息,free(arr)?可能釋放整個數組(因內存塊連續),但這是未定義行為,依賴具體實現。(內置類型數組

  • 析構函數未被調用 → 資源泄漏。
  • 釋放的地址錯誤(如未回退到頭部起始位置)→ 內存布局破壞,可能崩潰

三、高階

1、如何重載?new?和?delete?運算符?

?應用場景

  • 內存池優化(減少碎片)
  • 調試內存泄漏(記錄分配/釋放日志)
#include <iostream>
#include <cstdlib>
class MyClass {
public:static void* operator new(size_t size) {std::cout << "Custom new operator called" << std::endl;return std::malloc(size);}static void operator delete(void* ptr) {std::cout << "Custom delete operator called" << std::endl;std::free(ptr);}~MyClass() {std::cout << "Destructor called" << std::endl;}
};
int main() {MyClass* obj = new MyClass();delete obj;return 0;
}

2、?如何捕獲new過程異常并處理

int main() {try {while (true) {int* ptr = new int[1000000];}} catch (const std::bad_alloc& e) {std::cout << "Memory allocation failed: " << e.what() << std::endl;}return 0;
}

3、?deletedelete[]有何區別?

delete釋放單個對象;delete[]釋放數組

delete調用一次析構函數;delete[]對數組中每個元素調用析構函數

對數組使用delete會導致內存泄漏或崩潰。

4、new[]如何知道要調用多少次析構函數?

頭部信息存儲:當分配自定義類型數組時,new[]會在內存塊頭部額外存儲數組長度(如4/8字節)

delete[]根據頭部信息確定析構次數,再釋放完整內存塊

MyClass* arr = new MyClass[5];  
// 內存布局:[長度=5][對象1][對象2]...[對象5]
delete[] arr;  // 讀取長度5,調用5次析構函數

內置類型數組:若數組元素是基本類型(如?int),某些編譯器可能不添加頭部信息(因無需調用析構函數),直接分配連續內存?

?5、設計一個內存泄漏檢測工具,如何跟蹤new/delete的使用?

?重載全局operator new/delete:記錄分配/釋放的地址和大小。

哈希表跟蹤:維護分配記錄,檢測未配對的newdelete

std::map<void*, size_t> allocMap;  
void* operator new(size_t size) {  void* p = malloc(size);  allocMap[p] = size;  return p;  
}  
void operator delete(void* p) {  if (allocMap.erase(p)) free(p);  else logLeak();  // 檢測到未記錄釋放
}  

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

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

相關文章

Unity URP管線與HDRP管線對比

1. 渲染架構與底層技術 URP 渲染路徑&#xff1a; 前向渲染&#xff08;Forward&#xff09;&#xff1a;默認單Pass前向&#xff0c;支持少量實時光源&#xff08;通常4-8個逐物體&#xff09;。 延遲渲染&#xff08;Deferred&#xff09;&#xff1a;可選但功能簡化&#…

JDK8卸載與安裝教程(超詳細)

JDK8卸載與安裝教程&#xff08;超詳細&#xff09; 最近學習一個項目&#xff0c;需要使用更高級的JDK&#xff0c;這里記錄一下卸載舊版本與安裝新版本JDK的過程。 JDK8卸載 以windows10操作系統為例&#xff0c;使用快捷鍵winR輸入cmd&#xff0c;打開控制臺窗口&#xf…

python爬蟲:DrissionPage實戰教程

如果本文章看不懂可以看看上一篇文章&#xff0c;加強自己的基礎&#xff1a;爬蟲自動化工具&#xff1a;DrissionPage-CSDN博客 案例解析&#xff1a; 前提&#xff1a;我們以ChromiumPage為主&#xff0c;寫代碼工具使用Pycharm&#xff08;python環境3.9-3.10&#xff09; …

07-01-自考數據結構(20331)- 排序-內部排序知識點

內部排序算法是數據結構核心內容,主要包括插入類(直接插入、希爾)、交換類(冒泡、快速)、選擇類(簡單選擇、堆)、歸并和基數五大類排序方法。 知識拓撲 知識點介紹 直接插入排序 定義:將每個待排序元素插入到已排序序列的適當位置 算法步驟: 從第二個元素開始遍歷…

Go語言-初學者日記(八):構建、部署與 Docker 化

&#x1f9f1; 一、go build&#xff1a;最基礎的構建方式 Go 的構建工具鏈是出了名的輕量、簡潔&#xff0c;直接用 go build 就能把項目編譯成二進制文件。 ? 構建當前項目 go build -o myapp-o myapp 指定輸出文件名默認會構建當前目錄下的 main.go 或 package main &a…

教程:如何使用 JSON 合并腳本

目錄 1. 介紹 2. 使用方法 3. 注意事項 4. 示例 5.完整代碼 1. 介紹 該腳本用于將多個 COCO 格式的 JSON 標注文件合并為一個 JSON 文件。COCO 格式常用于目標檢測和圖像分割任務&#xff0c;包含以下三個主要部分&#xff1a; "images"&#xff1a;圖像信息&a…

Java學習總結-緩沖流性能分析

測試用例&#xff1a; 分別使用原始的字節流&#xff0c;以及字節緩沖流復制一個很大的視頻。 測試步驟&#xff1a; 在這個分析性能需要一個記錄時間的工具&#xff1a;這個是記錄1970-1-1 00&#xff1a;00&#xff1a;00到現在的總毫秒值。 long start System.currentT…

流影---開源網絡流量分析平臺(五)(成果展示)

目錄 前沿 攻擊過程 前沿 前四章我們已經成功安裝了流影的各個功能&#xff0c;那么接下來我們就看看這個開源工具的實力&#xff0c;本實驗將進行多個攻擊手段&#xff08;ip掃描&#xff0c;端口掃描&#xff0c;sql注入&#xff09;攻擊靶機&#xff0c;來看看流影的態感效…

vs環境中編譯osg以及osgQt

1、下載 OpenSceneGraph 獲取源代碼 您可以通過以下方式獲取 OSG 源代碼: 官網下載:https://github.com/openscenegraph/OpenSceneGraph/releases 使用 git 克隆: git clone https://github.com/openscenegraph/OpenSceneGraph.git 2、下載必要的第三方依賴庫 依賴庫 ht…

Unity:標簽(tags)

為什么需要Tags&#xff1f; 在游戲開發中&#xff0c;游戲對象&#xff08;GameObject&#xff09;數量可能非常多&#xff0c;比如玩家、敵人、子彈等。開發者需要一種簡單的方法來區分這些對象&#xff0c;并根據它們的類型執行不同的邏輯。 核心需求&#xff1a; 分類和管…

【C++11】lambda

lambda lambda表達式語法 lambda表達式本質是一個匿名函數對象&#xff0c;跟普通函數不同的是它可以定義在函數內部。lambda表達式語法使用層而言沒有類型&#xff0c;所以一般是用auto或者模板參數定義的對象去接收lambda對象。 lambda表達式的格式&#xff1a;[capture-l…

fpga:分秒計時器

任務目標 分秒計數器核心功能&#xff1a;實現從00:00到59:59的循環計數&#xff0c;通過四個七段數碼管顯示分鐘和秒。 復位功能&#xff1a;支持硬件復位&#xff0c;將計數器歸零并顯示00:00。 啟動/暫停控制&#xff1a;通過按鍵控制計時的啟動和暫停。 消抖處理&#…

《UNIX網絡編程卷1:套接字聯網API》第6章 IO復用:select和poll函數

《UNIX網絡編程卷1&#xff1a;套接字聯網API》第6章 I/O復用&#xff1a;select和poll函數 6.1 I/O復用的核心價值與適用場景 I/O復用是高并發網絡編程的基石&#xff0c;允許單個進程/線程同時監控多個文件描述符&#xff08;套接字&#xff09;的狀態變化&#xff0c;從而高…

SpringBoot+vue前后端分離整合sa-token(無cookie登錄態 詳細的登錄流程)

SpringBootvue前后端分離整合sa-token&#xff08;無cookie登錄態 & 詳細的登錄流程&#xff09; 1.介紹sa-token1.1 框架定位1.2 核心優勢 2.如何整合sa-token3.如何進行無cookie模式登錄3.1后端3.1.1 VO層3.1.2 Controller層3.1.3 Service層 3.2前端3.2.1 登錄按鈕自定義…

MYOJ_1171:(洛谷P1075)[NOIP 2012 普及組] 質因數分解(數學相關,質數與約數基礎)

題目描述 已知正整數 n 是兩個不同的質數的乘積&#xff0c;試求出兩者中較大的那個質數。 1≤n≤210^9 輸入 輸入一個正整數 n。 輸出 輸出一個正整數 p&#xff0c;即較大的那個質數。 樣例輸入輸出 輸入&#xff1a;21 輸出&#xff1a;7 思路: 為了節約時間與…

Python語言的測試用例設計

Python語言的測試用例設計 引言 隨著軟件開發的不斷進步&#xff0c;測試在軟件開發生命周期中的重要性日益凸顯。測試用例設計是軟件測試的核心&#xff0c;它為軟件系統的驗證和驗證提供了實施的基礎。在Python語言中&#xff0c;由于其簡潔明了的語法和強大的內置庫&#…

SpringKafka消息消費:@KafkaListener與消費組配置

文章目錄 引言一、Spring Kafka消費者基礎配置二、KafkaListener注解使用三、消費組配置與負載均衡四、手動提交偏移量五、錯誤處理與重試機制總結 引言 Apache Kafka作為高吞吐量的分布式消息系統&#xff0c;在大數據處理和微服務架構中扮演著關鍵角色。Spring Kafka為Java開…

VMware 虛報化Ubuntu 卡成一B,如何接招?

故事背景 Win10 專業版 安裝VMware pro ,虛擬化出一個Window10&#xff0c;另一個是UBuntu.自從使用起來去不去就卡死。開始是以為驅動或者升級造成的&#xff0c;重新安裝一段時間問題照舊。更氣人的這種現象具有不定期性&#xff0c;說不定什么時候就來這么一出。 直接解決方…

cloud項目批量修改主機號

當clone了一個cloud項目后&#xff0c;要把別人的主機號全部改成自己的&#xff0c;非常麻煩 在項目根目錄下&#xff0c;啟動 Git Bash。在 Git Bash 終端中使用原始的 Unix 命令&#xff1a; find . -type f -exec sed -i s/127\.0\.0\.1/132.168.190.163/g {} 其中127.0.…

微信小程序使用 Vant Weapp 組件庫教程

在微信小程序項目中使用 Vant 組件庫&#xff08;Vant Weapp&#xff09;主要包括以下幾個步驟&#xff1a; 1. 初始化項目并安裝 Vant Weapp 初始化 npm 在項目根目錄下運行以下命令&#xff0c;生成 package.json&#xff1a; npm init -y安裝 Vant Weapp 執行以下命令安裝 V…