C++ 淺拷貝 和深拷貝的知識點講解

好問題 👍,這個就是理解 = delete 的關鍵前置知識。
我從 C語言基礎 出發,像教科書一樣,從零講給你聽。


1. 什么是“拷貝”?

在 C++ 中,當你有一個對象 A,然后寫:

Device_Info a;
Device_Info b = a;   // 或者 b(a);

這就是在“拷貝一個對象”。
系統會幫你把 a 的內容復制到 b 里,這個過程叫做 拷貝構造

同樣地,如果寫:

Device_Info c;
c = a;   // 已經存在的對象 c,用 a 的內容覆蓋它

這就是“拷貝賦值”。


2. 淺拷貝(Shallow Copy)

👉 定義:只復制“表面數據”,也就是 成員變量的值

舉個例子:

struct Data {int* ptr;
};Data a;
a.ptr = new int(42);   // a 里面的指針指向堆上的一個整數Data b = a;   // 系統默認的拷貝(淺拷貝)

這時候 a.ptrb.ptr 指向同一塊內存

  • 修改 *a.ptr 會影響 *b.ptr
  • 如果兩個對象都去 delete ptr; → 就會出現 二次釋放(double free) 錯誤。

👉 這就是淺拷貝的風險。


3. 深拷貝(Deep Copy)

👉 定義:不光復制指針的值,還會 分配一塊新的內存,并復制數據內容

改寫上面的例子:

struct Data {int* ptr;// 自己實現拷貝構造函數(深拷貝)Data(const Data& other) {ptr = new int(*other.ptr); // 新開一塊內存,復制內容}
};

這樣 a.ptrb.ptr 各自獨立:

  • 改變 a 不會影響 b
  • 各自釋放時也不會沖突。

4. 為什么要禁用拷貝?(= delete)

有些類(比如你說的 Device_Info)是全局單例,不能被“復制”出新對象。

如果你允許淺拷貝:

  • 很可能復制出來多個“全局唯一”的對象,狀態混亂。

如果你允許深拷貝:

  • 邏輯上也沒意義:全局設備信息只有一份,復制出來的副本沒法保持同步。

👉 所以直接禁用拷貝:

Device_Info(const Device_Info&) = delete;       // 禁用拷貝構造
Device_Info& operator=(const Device_Info&) = delete; // 禁用拷貝賦值

這樣一來,編譯器遇到下面的代碼時就會報錯:

Device_Info a;
Device_Info b = a;   // ? 編譯錯誤
Device_Info c;
c = a;               // ? 編譯錯誤

保證了你的 Device_Info 只有一個實例,不會被復制。


? 一句話總結

  • 淺拷貝 = 復制指針值 → 多個對象共享同一資源(風險大)。
  • 深拷貝 = 復制指針指向的內容 → 每個對象都有獨立資源(安全,但開銷大)。
  • = delete = 禁止拷貝 → 防止“本不該復制的對象”被復制(最安全)。

要不要我給你畫個“淺拷貝 vs 深拷貝”的圖解,形象一點?




好👌 那我從 C 角度 慢慢過渡到 C++ 淺拷貝 / 深拷貝,讓你完全能理解。


1. C 里拷貝變量

在 C 里,我們寫:

int a = 10;
int b = a;  // 把 a 的值拷貝到 b

這里 b 得到的是一個獨立的副本,修改 b 不會影響 a
這就相當于 值的復制


2. 指針和數組的情況

再來看指針或數組:

char *p = malloc(10);
char *q = p;   // q 和 p 指向同一塊內存

現在 pq 共享一塊內存。如果你通過 q 修改數據,p 看到的也會變。
這就不是復制內容,而是復制了“地址” → 淺拷貝


3. 到 C++ 的對象

在 C++ 里,對象可以有很多成員(比如內存指針、數組、文件句柄等)。
當你寫:

Device_Info a;
Device_Info b = a;  // 調用拷貝構造函數

編譯器默認會幫你“一個字段一個字段”地復制 → 淺拷貝

  • 如果對象里有普通的 int、double,這沒問題。
  • 如果對象里有指針,那就危險了!因為兩個對象的指針會指向同一塊內存,可能導致 二次釋放 (double free)懸空指針

4. 淺拷貝 vs 深拷貝

淺拷貝(shallow copy)

只復制“指針的值”,不復制指針指向的內容。
結果:多個對象共享同一塊資源。

例子:

struct A {int *data;
};
A a;
a.data = new int(5);A b = a;   // 淺拷貝:b.data 和 a.data 指向同一塊內存

這樣當 abdelete data 時就會崩潰。


深拷貝(deep copy)

不但復制指針的值,還復制一份獨立的內容。
結果:每個對象有自己獨立的資源。

例子:

struct A {int *data;// 自己實現拷貝構造函數A(const A& other) {data = new int(*other.data); // 復制一份內容}
};

這樣 ab 各自有自己的內存,互不影響。


5. 為什么要 = delete

有時候我們覺得拷貝很危險(比如全局唯一的資源),
干脆禁止拷貝,寫:

Device_Info& operator=(const Device_Info&) = delete;

意思是:禁止拷貝賦值,編譯器一旦遇到 a = b 就會報錯。
這就是一種防止“淺拷貝出錯”的安全寫法。


📌 總結:

  • 淺拷貝:只復制指針本身,多個對象共享一塊資源 → 可能危險。
  • 深拷貝:復制指針指向的內容,每個對象有自己的資源 → 安全但需要寫代碼實現。
  • 禁用拷貝 (= delete):當資源不能被復制時(比如全局唯一對象),直接禁止。

要不要我給你寫一個小的 C 風格 vs C++ 淺拷貝 vs 深拷貝的完整對比代碼,你直接跑一下就能看到區別?


深拷貝案例

好的,我們用一個 非常簡單的例子 來說明淺拷貝和深拷貝的區別,不涉及圖像,只用一個 int* 指針就夠了。


例子:管理一個整數的類

#include <iostream>class MyInt {
public:int* value;// 構造函數MyInt(int v) {value = new int(v);}// 析構函數~MyInt() {delete value;}// 深拷貝構造函數MyInt(const MyInt& other) {value = new int(*other.value); // 申請新內存,復制內容}void print() {std::cout << *value << std::endl;}
};int main() {MyInt a(10);MyInt b = a; // 調用深拷貝構造函數a.print(); // 10b.print(); // 10*a.value = 20; // 修改 aa.print();     // 20b.print();     // 10(b 不受影響)return 0;
}

🔹 分析

  1. a.valueb.value 各自指向獨立內存
  2. 修改 a.value 不會影響 b.value
  3. 析構時安全,不會 double free

如果去掉深拷貝構造函數,默認的拷貝構造是淺拷貝:

MyInt(const MyInt& other) = default;

結果就是:

  • a.valueb.value 指向同一塊內存
  • 修改 a 會影響 b
  • 兩個對象析構時都會 delete 同一塊內存 → 崩潰

這個例子非常直觀,用一個指針就能理解淺拷貝、深拷貝的區別


如果你需要,我可以幫你畫一張 指針指向的內存圖,形象展示淺拷貝和深拷貝的區別,讓你一眼就明白。

你想讓我畫嗎?

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

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

相關文章

Arkts加載網頁url的pdf發票黑屏問題

使用模擬器會閃退&#xff0c;連接真機預覽正常import { HMRouter, HMRouterMgr, HMParamType, HMPageParam } from "hadss/hmrouter"; import { BaseTitle, RouterConstants } from "commonlib"; import { webview } from kit.ArkWeb; import { PDFView }…

使用 mongosh 設置 MongoDB 賬號密碼

可以使用 mongosh(MongoDB 6.0+ 的現代Shell)來設置賬號密碼。mongosh 是官方推薦的新一代MongoDB Shell工具,替代了傳統的 mongo 命令。 【本人博文提到的ip均為隨機生成的,不是實際的ip,僅供參考,如有雷同,純屬巧合】 使用 mongosh 設置 MongoDB 賬號密碼 1. 首先連…

HTML+CSS:浮動詳解

在HTMLCSS布局中&#xff0c;浮動&#xff08;float&#xff09; 是一種經典的布局技術&#xff0c;用于控制元素在頁面中的排列方式。它最初設計用于實現文字環繞圖片的效果&#xff0c;后來被廣泛用于復雜布局&#xff0c;但隨著Flexbox和Grid的興起&#xff0c;其使用場景有…

GPIO初始化及調用

下面把 HAL 庫 和 標準外設庫&#xff08;SPL&#xff09; 初始化 GPIO 點亮/熄滅 LED 的完整步驟、示例代碼和常用 API 逐一說清楚。用例默認 PC13 接 LED&#xff08;藍板常見&#xff1b;低電平點亮&#xff0c;高電平熄滅——若板子相反&#xff0c;只把寫 1/0 對調即可&am…

【GPT入門】第48課 LlamaFacotory 合并原模型與LoRA模型

【GPT入門】第48課 LlamaFacotory 合并原模型與LoRA模型1.合并原模型與LoRA訓練的增量模型2. 測試模型1.合并原模型與LoRA訓練的增量模型 llamafactory-cli webui 執行合并 合并后模型大小 (base) rootautodl-container-b4b04ea4f2-b5ee47d1:~# du -sh /root/autodl-tmp/mod…

Python爬蟲實戰:研究tumblr,構建博客平臺數據采集分析系統

1. 引言 1.1 研究背景 在信息爆炸的時代,社交媒體平臺已成為人們獲取信息、表達觀點和進行社交互動的主要渠道。這些平臺上積累的海量數據包含了用戶偏好、社會趨勢、文化現象等豐富信息,對學術研究、市場分析、產品開發等領域具有重要價值。 Tumblr 作為一個綜合性的輕博客…

集成算法學習總結

一、集成學習基礎認知 核心思想&#xff1a;集成學習&#xff08;ensemble learning&#xff09;通過構建并結合多個個體學習器來完成學習任務&#xff0c;類似于 “多個專家共同決策”&#xff0c;通常比單個學習器的性能更優。其核心邏輯是利用多個學習器的優勢互補&#xff…

線程安全的產生以及解決方案

線程安全原子性&#xff08;Atomicity&#xff09;、可見性&#xff08;Visibility&#xff09;、有序性&#xff08;Ordering&#xff09; 是保證線程安全的三大核心要素 —— 線程安全問題的本質&#xff0c;幾乎都是這三個特性中的一個或多個被破壞導致的。操作不會被 “中途…

Spring Cloud Netflix學習筆記01

文章目錄前言一、微服務概述什么是微服務&#xff1f;微服務與微服務架構微服務優缺點優點缺點微服務技術棧有那些&#xff1f;二.SpringCloud入門概述SpringCloud是什么&#xff1f;SpringCloud和SpringBoot的關系Dubbo 和 SpringCloud技術選型總結SpringCloud能干嘛&#xff…

專題:2025母嬰行業消費洞察與分齡營養趨勢報告|附40 +份報告PDF、交互圖表數據匯總下載

原文鏈接&#xff1a;https://tecdat.cn/?p43654 當95后媽媽拿著計算器對比DHA純度&#xff0c;當爸爸們為“防紅屁屁紙尿褲”貨比三家&#xff0c;母嬰行業的風向早就變了。從“一把奶粉喂到3歲”到“按月齡定制營養包”&#xff0c;從“進口就好”到“看專利數據下單”&…

redhat6/centos6 配置yum源

由于RHEL6/centos6系統官方早就停止通知維護了&#xff0c;公司的開發服務器有比較老&#xff0c;發現竟然scp都沒有裝。。。今天配置個本地yum源&#xff0c;安裝一下常規軟件和開發環境比較簡單&#xff0c;直接上代碼1.上傳一個centos6的iso文件CentOS-6.5-x86_64-bin-DVD1.…

day31 SQLITE

數據庫相關函數數據庫創建int sqlite3_open( const char *filename, sqlite3 **ppDb);功能&#xff1a;打開數據庫&#xff0c;不存在則創建參數&#xff1a;const char *filename 數據庫名sqlite3 **ppDb 二級指針&#xff0c;傳出ppDb數據庫的一級指…

嵌入式-SPI番外之按鈕驅動程序的編寫-Day15

目錄 一、按鈕簡單操作回憶 二、按鈕新操作實現 &#xff08;1&#xff09;按鈕的點擊實現燈亮/滅 ①連接電路 ②初始化板載LED和按鈕 ③按鈕程序的基本原理&#xff08;核心仍為0亮/1滅&#xff09; ④按鈕消抖的原理 三、按鈕封裝的操作-點擊&#xff0c;雙擊&#xf…

星域智鏈科技:用科技點亮生活,以 AI 拓展無限可能

星域智鏈科技&#xff08;東莞市&#xff09;有限公司簡介 星域智鏈科技&#xff08;東莞市&#xff09;有限公司&#xff0c;理念是 讓科技便利生活、豐富生活&#xff0c;專注于科技、AI領域。 全場景 GPS 定位器 —— 精準追蹤&#xff0c;守護安全&#xff0c;適用于車輛…

國內代理IP在SEO行業中的應用

隨著互聯網的快速發展&#xff0c;SEO&#xff08;搜索引擎優化&#xff09;已經成為了數字營銷的重要組成部分。無論是企業還是個人站長&#xff0c;都希望通過SEO提升自己網站的排名和流量。然而&#xff0c;隨著競爭的激烈&#xff0c;傳統的SEO優化手段已經逐漸顯現出局限性…

Linux + arm 內存屏障

ARM 硬件層的屏障指令DMB (Data Memory Barrier)&#xff1a;保證在它之前的內存訪問&#xff08;符合給定域/類型&#xff09;在它之后的內存訪問之前對可見性排序。常用域&#xff1a;ish&#xff08;Inner Shareable&#xff09;&#xff0c;sy&#xff08;system-wide&…

網絡安全中的遠程控制活動檢測與防御策略

本文還有配套的精品資源&#xff0c;點擊獲取 簡介&#xff1a;遠程控制技術在IT領域中用于網絡連接和設備操作&#xff0c;但同樣被黑客利用進行非法入侵。端口占用情況是識別遠程控制活動的關鍵因素&#xff0c;使用工具如"cports"可以監控這些端口。系統中未知…

UIGestureRecognizer 各個子類以及其作用

在 iOS 里&#xff0c;UIGestureRecognizer 是一個抽象基類&#xff0c;專門用來處理手勢事件。它本身不能直接用&#xff0c;必須用它的 子類。這些子類分別對應常見的手勢識別器。常見的 UIGestureRecognizer 子類及作用1. UITapGestureRecognizer作用&#xff1a;點擊手勢&a…

計算機網絡 HTTPS 全流程

HTTPS 通信的全流程&#xff08;特別是 TLS 握手階段&#xff09;中使用的三個隨機數是保障安全性的核心設計&#xff0c;不能隨意減少。每個隨機數都承擔著至關重要的安全職責。下面詳細解釋 HTTPS 全流程&#xff0c;并重點分析這三個隨機數的作用和必要性&#xff1a;&#…

DL00271-基于YOLOv11的激光雷達LiDAR船舶目標檢測含完整數據集

【CSDN推薦】基于YOLOv11的激光雷達&#xff08;LiDAR&#xff09;船舶目標檢測——含完整數據集&#xff01;&#x1f6a2; 科研人員必看&#xff01; 高校老師、學生和研究者們&#xff0c;前沿技術來了&#xff01;本論文利用YOLOv11模型&#xff0c;結合激光雷達&#xff0…