最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

c++中智能指針與裸指針的實(shí)現(xiàn)

 更新時(shí)間:2026年02月27日 16:17:17   作者:CSDN_RTKLIB  
本文主要介紹了c++中智能指針與裸指針的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

現(xiàn)在有一個(gè)shared共享指針,通過.get方法賦值給一個(gè)裸指針,如果共享指針本身聲明周期到了,裸指針還能繼續(xù)使用嗎?裸指針會(huì)影響智能指針生命周期嗎

一、說明

在這種情況下,當(dāng)shared_ptr的生命周期結(jié)束時(shí),它所管理的資源會(huì)根據(jù)引用計(jì)數(shù)來決定是否銷毀:

  1. 核心原理shared_ptr通過引用計(jì)數(shù)管理資源,當(dāng)最后一個(gè)shared_ptr被銷毀時(shí),引用計(jì)數(shù)變?yōu)?,資源會(huì)被自動(dòng)釋放。

  2. 裸指針的影響:通過get()方法獲取的裸指針只是資源的一個(gè)普通指針,它不會(huì)影響shared_ptr的引用計(jì)數(shù)。

  3. 具體行為

    • 當(dāng)shared_ptr生命周期結(jié)束時(shí),無論裸指針是否還在使用,shared_ptr都會(huì)正常遞減引用計(jì)數(shù)
    • 如果此時(shí)引用計(jì)數(shù)變?yōu)?,資源會(huì)被銷毀
    • 這會(huì)導(dǎo)致裸指針變成懸空指針(dangling pointer),繼續(xù)使用會(huì)產(chǎn)生未定義行為

示例說明:

#include <memory>

int main() {
    int* raw_ptr = nullptr;
    
    {
        std::shared_ptr<int> shared_ptr = std::make_shared<int>(42);
        raw_ptr = shared_ptr.get();  // 獲取裸指針
        // 此時(shí)引用計(jì)數(shù)為1
    }  // shared_ptr生命周期結(jié)束,引用計(jì)數(shù)減為0,資源被銷毀
    
    // 此時(shí)raw_ptr成為懸空指針,訪問它是危險(xiǎn)的
    // *raw_ptr = 10;  // 未定義行為!
    return 0;
}

結(jié)論shared_ptr會(huì)正常銷毀其管理的資源(當(dāng)引用計(jì)數(shù)為0時(shí)),與通過get()獲取的裸指針是否還在使用無關(guān)。這種用法存在風(fēng)險(xiǎn),可能導(dǎo)致懸空指針問題,應(yīng)盡量避免。

二、測(cè)試

執(zhí)行下面代碼

#include <memory>
#include <iostream>
int main() {
    int* raw_ptr = nullptr;
    {
        std::shared_ptr<int> shared_ptr = std::make_shared<int>(42);
        raw_ptr = shared_ptr.get();  
    }  
    std::cout << *raw_ptr << std::endl;
    *raw_ptr = 10; 
    std::cout << *raw_ptr << std::endl;
    return 0;
}

第一個(gè)輸出為垃圾值,第二次輸出是10,程序也沒有崩潰,這是為什么?

要理解這個(gè)現(xiàn)象,核心是抓住 未定義行為(Undefined Behavior, UB)的本質(zhì)——不是“必然崩潰”,而是“結(jié)果不可預(yù)測(cè)”。你的代碼能輸出10而非崩潰,只是一種“偶然的合法表象”,背后是內(nèi)存管理的底層邏輯在起作用:

第一步:先理清內(nèi)存的“銷毀”到底是什么

當(dāng) shared_ptr 生命周期結(jié)束時(shí),它做的事情是:

  1. 引用計(jì)數(shù)減為0,觸發(fā) 資源釋放(調(diào)用 delete 銷毀堆上的 int 對(duì)象);
  2. 釋放的本質(zhì)是:告訴操作系統(tǒng)“這塊堆內(nèi)存可以被重新分配給其他代碼使用了”,而不是“把這塊內(nèi)存的數(shù)據(jù)清零”或“標(biāo)記為不可訪問”。

簡(jiǎn)單說:堆內(nèi)存被 delete 后,數(shù)據(jù)可能還留在原地,只是它的“使用權(quán)”被回收了——你后續(xù)再訪問它,屬于“非法侵占”,但操作系統(tǒng)不會(huì)立刻阻止(除非觸發(fā)了內(nèi)存保護(hù)機(jī)制)。

第二步:為什么第一個(gè)輸出是“垃圾值”,第二個(gè)是10?

我們逐行拆解代碼執(zhí)行過程:

int* raw_ptr = nullptr;

{
    std::shared_ptr<int> shared_ptr = std::make_shared<int>(42);
    raw_ptr = shared_ptr.get();  // raw_ptr指向堆上的int(值為42)
}  // shared_ptr銷毀:堆上的int被delete(使用權(quán)回收),但內(nèi)存數(shù)據(jù)可能還是42

// 第一次訪問*raw_ptr:非法訪問已釋放的堆內(nèi)存
std::cout << *raw_ptr << std::endl;  // 輸出“垃圾值”或42,都是UB的表現(xiàn)
// 第二次訪問:給已釋放的內(nèi)存賦值10
*raw_ptr = 10;  // 非法賦值,但內(nèi)存未被其他代碼復(fù)用,所以能“成功”寫入10
std::cout << *raw_ptr << std::endl;  // 讀取剛寫入的10,輸出10

關(guān)鍵原因:

  1. 第一個(gè)輸出“垃圾值”:可能是堆內(nèi)存被 delete 后,數(shù)據(jù)沒有被覆蓋(比如還是42),也可能是被 操作系統(tǒng)/其他代碼輕微修改(變成隨機(jī)值)——本質(zhì)是“讀取了不屬于你程序合法擁有的內(nèi)存”,結(jié)果隨機(jī),所以叫“垃圾值”。
  2. 第二個(gè)輸出10:因?yàn)樵谫x值 *raw_ptr = 10 時(shí),這塊堆內(nèi)存還沒被其他代碼復(fù)用(操作系統(tǒng)沒把它分配給別的變量),所以你的程序“碰巧”還能操作這塊內(nèi)存(寫入10),后續(xù)讀取自然能拿到10。

這就像:你租了個(gè)房子(堆內(nèi)存),到期后退租(delete),鑰匙被房東收回(使用權(quán)回收),但你還能偷偷溜進(jìn)去(非法訪問)——房子里的家具(數(shù)據(jù))可能還在(垃圾值),你甚至能換套家具(賦值10),只要房東還沒把房子租給別人(內(nèi)存未復(fù)用),就不會(huì)被發(fā)現(xiàn)。

第三步:為什么程序沒崩潰?

崩潰的本質(zhì)是:程序觸發(fā)了操作系統(tǒng)的“內(nèi)存保護(hù)機(jī)制”(比如訪問了“完全不存在的內(nèi)存地址”“已被其他進(jìn)程占用的內(nèi)存”“無權(quán)限訪問的內(nèi)存”)。

而你的代碼中:

  • raw_ptr 指向的是 曾經(jīng)合法的堆內(nèi)存地址(不是野指針指向的隨機(jī)無效地址);
  • 這塊內(nèi)存還沒被 操作系統(tǒng)分配給其他程序/線程,所以你的程序?qū)λ?ldquo;讀/寫”操作沒有觸發(fā)內(nèi)存保護(hù),自然不會(huì)崩潰。

但這是極端危險(xiǎn)的“運(yùn)氣”——如果在 shared_ptr 銷毀后、你訪問 raw_ptr 之前,有其他代碼(比如函數(shù)調(diào)用、動(dòng)態(tài)內(nèi)存分配)復(fù)用了這塊堆內(nèi)存,那么:

  • 讀取 *raw_ptr 會(huì)拿到其他代碼的數(shù)據(jù)(垃圾值);
  • 寫入 *raw_ptr = 10 會(huì)覆蓋其他代碼的數(shù)據(jù),導(dǎo)致程序邏輯錯(cuò)亂(比如變量值莫名改變、函數(shù)返回錯(cuò)誤結(jié)果);
  • 更糟的是:如果這塊內(nèi)存被 操作系統(tǒng)標(biāo)記為“不可訪問”(比如后續(xù)分配給了更高權(quán)限的進(jìn)程),此時(shí)訪問會(huì)直接觸發(fā) 段錯(cuò)誤(SIGSEGV),程序崩潰。

核心結(jié)論:未定義行為的“不可預(yù)測(cè)性”

你的代碼能運(yùn)行且輸出10,不代表程序正確,只是UB的一種“溫和表現(xiàn)”。UB的可能結(jié)果包括:

  1. 程序正常運(yùn)行,輸出預(yù)期值(如你的情況);
  2. 輸出垃圾值;
  3. 程序崩潰(段錯(cuò)誤);
  4. 程序邏輯錯(cuò)亂(后續(xù)代碼運(yùn)行異常);
  5. 甚至在不同編譯器(GCC/Clang/MSVC)、不同優(yōu)化等級(jí)(O0/O2)下表現(xiàn)完全不同。

比如:如果開啟編譯器優(yōu)化(-O2),可能會(huì)直接優(yōu)化掉 *raw_ptr 的訪問,輸出完全無意義的值,甚至程序崩潰——因?yàn)榫幾g器會(huì)假設(shè)“程序不會(huì)執(zhí)行UB”,從而進(jìn)行激進(jìn)優(yōu)化。

如何避免這種問題?

永遠(yuǎn)不要用 shared_ptr.get() 獲取裸指針后,在 shared_ptr 銷毀后使用。正確做法:

  1. shared_ptr 的生命周期覆蓋裸指針的使用(擴(kuò)大 shared_ptr 的作用域);
  2. 若需長期持有“弱引用”,用 std::weak_ptr(不會(huì)增加引用計(jì)數(shù),可通過 lock() 安全判斷資源是否存在);
  3. 直接傳遞 shared_ptr(而非裸指針),依賴其引用計(jì)數(shù)管理生命周期。

正確示例(擴(kuò)大 shared_ptr 作用域):

int main() {
    std::shared_ptr<int> shared_ptr = std::make_shared<int>(42);
    int* raw_ptr = shared_ptr.get();  // 裸指針的使用被shared_ptr生命周期覆蓋
    
    std::cout << *raw_ptr << std::endl;  // 合法,輸出42
    *raw_ptr = 10;                       // 合法,修改的是shared_ptr管理的資源
    std::cout << *raw_ptr << std::endl;  // 合法,輸出10
    
    return 0;
}  // shared_ptr銷毀,資源正常釋放,無懸空指針

最終總結(jié)

你看到的“第一個(gè)輸出垃圾值、第二個(gè)輸出10且不崩潰”,是 未定義行為下的偶然結(jié)果

  • 堆內(nèi)存被釋放后未被復(fù)用,導(dǎo)致你能非法讀寫;
  • 程序未觸發(fā)內(nèi)存保護(hù)機(jī)制,所以沒崩潰。

但這種代碼是“定時(shí)炸彈”——任何微小的改動(dòng)(比如增加函數(shù)調(diào)用、開啟優(yōu)化、多線程環(huán)境)都可能讓程序崩潰或邏輯錯(cuò)亂。永遠(yuǎn)不要依賴UB的表現(xiàn),遵守智能指針的使用規(guī)則,避免懸空指針。

三、問題

int* raw_ptr = nullptr;
std::cout << *raw_ptr << std::endl;

1.上面這樣打印raw_ptr會(huì)程序崩潰,為什么
2.講下野指針

問題1:int* raw_ptr = nullptr; std::cout << *raw_ptr << std::endl; 為什么會(huì)崩潰?

核心原因:nullptr 指向的是“明確無效的內(nèi)存地址”,解引用會(huì)觸發(fā)操作系統(tǒng)的內(nèi)存保護(hù)機(jī)制,直接導(dǎo)致程序崩潰。

我們分3層講清楚:

  1. nullptr 的本質(zhì)nullptr 是C++11引入的“空指針常量”,它不指向任何合法的內(nèi)存地址(通常對(duì)應(yīng)底層的 0x0 地址,或操作系統(tǒng)預(yù)留的“無效地址區(qū)間”)。
    它和你之前代碼中“指向已釋放堆內(nèi)存的指針”完全不同:前者是“明確的無效地址”,后者是“曾經(jīng)有效、現(xiàn)在已被回收的地址”。

  2. 解引用 nullptr 的后果:操作系統(tǒng)會(huì)對(duì)內(nèi)存地址進(jìn)行權(quán)限管理——nullptr 指向的地址是“程序絕對(duì)無權(quán)訪問”的(相當(dāng)于“不存在的地址”)。
    當(dāng)你執(zhí)行 *raw_ptr 時(shí),程序試圖讀寫這個(gè)無效地址,會(huì)直接觸發(fā) 段錯(cuò)誤(SIGSEGV) 或“訪問違規(guī)”,操作系統(tǒng)會(huì)強(qiáng)制終止程序(也就是“崩潰”)。

  3. 和你之前代碼的對(duì)比

    • 之前的 raw_ptr 指向“已釋放但未復(fù)用的堆內(nèi)存”:地址本身是“合法存在”的,只是程序沒有使用權(quán),所以可能不崩潰(運(yùn)氣好);
    • 現(xiàn)在的 raw_ptr = nullptr:地址本身是“非法無效”的,程序一訪問就觸發(fā)內(nèi)存保護(hù),必然崩潰。

簡(jiǎn)單比喻:

  • 之前的情況:你溜進(jìn)“已退租但未租給別人的房子”,可能沒被發(fā)現(xiàn);
  • 現(xiàn)在的情況:你試圖闖進(jìn)“根本不存在的房子”(比如地址是0號(hào)),一靠近就被保安(操作系統(tǒng))攔下,直接“崩潰”。

問題2:野指針是什么?int* p; 是野指針嗎?

先明確:野指針的定義

野指針(Wild Pointer) 是指:指向“不確定地址”或“無效地址”,且沒有明確指向合法對(duì)象的指針
核心特征:指針的值是“隨機(jī)的、未定義的”,或指向的內(nèi)存已被釋放/回收,訪問它的行為是“未定義行為(UB)”。

再回答:int* p;是野指針嗎?—— 是的,大概率是!

分兩種場(chǎng)景看 int* p; 的行為:

  1. 局部變量場(chǎng)景(最常見):如果 p 是函數(shù)內(nèi)的局部變量(棧上分配),C++ 不會(huì)自動(dòng)初始化棧上的指針——p 的值是棧上的“垃圾值”(隨機(jī)的二進(jìn)制數(shù)據(jù))。
    這個(gè)隨機(jī)值可能指向:

    • 無效地址(比如 0x123456 這種不存在的地址);
    • 其他程序的內(nèi)存地址(無權(quán)訪問);
    • 本程序其他變量的地址(可能覆蓋其他數(shù)據(jù))。
      無論哪種,p 都是“指向不確定地址的指針”,屬于 典型的野指針
  2. 全局/靜態(tài)變量場(chǎng)景:如果 p 是全局變量或 static 變量,C++ 會(huì)默認(rèn)將其初始化為 nullptr(或 0),此時(shí)它是“空指針”,不是野指針(空指針是“明確的無效地址”,野指針是“不確定的無效地址”,兩者有區(qū)別)。

關(guān)鍵補(bǔ)充:野指針的3種常見來源

int* p;(未初始化的局部指針)只是野指針的一種,還有兩種更隱蔽的情況:

  1. 未初始化的局部指針(如 int* p;);
  2. 指向已釋放內(nèi)存的指針(如你之前代碼中,shared_ptr 銷毀后的 raw_ptr);
  3. 指針指向的對(duì)象被 delete 后,未置為 nullptr(比如 int* p = new int; delete p;,之后 p 還是指向原地址,變成野指針)。

容易混淆的概念:野指針 ≠ 空指針

類型特點(diǎn)訪問后果
野指針指針值隨機(jī)、不確定可能崩潰、可能亂改數(shù)據(jù)
空指針(nullptr指針值明確(指向無效地址)幾乎必然崩潰(可預(yù)測(cè)UB)

比如:

  • int* p;(局部變量)→ 野指針;
  • int* p = nullptr; → 空指針(不是野指針);
  • int* p = new int; delete p; → 野指針(指向已釋放地址)。

總結(jié)兩個(gè)問題的核心

  1. 解引用 nullptr 必崩潰:因?yàn)樗赶?ldquo;明確無效的地址”,觸發(fā)內(nèi)存保護(hù);
  2. 野指針是“指向不確定/無效地址的指針”:局部未初始化的 int* p; 是野指針,空指針不是野指針;
  3. 所有野指針的訪問都是UB,但后果不同(可能崩潰、可能亂碼、可能“看似正常”),但本質(zhì)都是極端危險(xiǎn)的。

補(bǔ)充:如何避免野指針?

  1. 指針聲明時(shí)立即初始化(要么指向合法對(duì)象,要么置為 nullptr):
    int* p = nullptr;int a = 10; int* p = &a;;
  2. 動(dòng)態(tài)內(nèi)存釋放后(deleteshared_ptr 銷毀),避免再使用對(duì)應(yīng)的指針;
  3. 若需長期持有“可能失效的引用”,用 std::weak_ptr(而非裸指針);
  4. 局部指針盡量縮短作用域,避免跨作用域傳遞裸指針。

總結(jié)

到此這篇關(guān)于c++中智能指針與裸指針的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)c++ 智能指針與裸指針內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++ 中私有繼承的作用

    C++ 中私有繼承的作用

    這篇文章主要介紹了C++ 中私有繼承的作用的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-10-10
  • C++中雙冒號(hào)::的作用淺析

    C++中雙冒號(hào)::的作用淺析

    在C++中經(jīng)常使用雙冒號(hào)::,很多朋友不知道是什么意思,這篇文章主要介紹了C++中雙冒號(hào)::的作用,需要的朋友可以參考下
    2018-06-06
  • C++中靜態(tài)庫與動(dòng)態(tài)庫的使用示例

    C++中靜態(tài)庫與動(dòng)態(tài)庫的使用示例

    在C/C++中使用庫的技術(shù),庫主要分為兩種類型:靜態(tài)庫和動(dòng)態(tài)庫,本文主要介紹了C++中靜態(tài)庫與動(dòng)態(tài)庫的使用示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09
  • C++ 編寫DLL文件給易語言調(diào)用方法

    C++ 編寫DLL文件給易語言調(diào)用方法

    在本文中我們給大家分享了C++ 編寫DLL文件給易語言調(diào)用的代碼和方法,需要的朋友們學(xué)習(xí)下。
    2019-01-01
  • 深入淺析C++多態(tài)性與虛函數(shù)

    深入淺析C++多態(tài)性與虛函數(shù)

    多態(tài)是指同樣的消息被不同的對(duì)象接收時(shí)導(dǎo)致不同的行為。本文通過實(shí)例代碼給大家介紹了C++多態(tài)性與虛函數(shù)的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2020-02-02
  • 一文解密C++中的多態(tài)機(jī)制

    一文解密C++中的多態(tài)機(jī)制

    眾所周知C++語言的三大特性:封裝、多態(tài)、繼承,其中多態(tài)就是去完成某個(gè)行為,但是會(huì)根據(jù)不同的對(duì)象產(chǎn)生不同的狀態(tài),下面小編就來帶大家深入了解一下C++的多態(tài)機(jī)制吧
    2023-07-07
  • C++實(shí)現(xiàn)LeetCode(91.解碼方法)

    C++實(shí)現(xiàn)LeetCode(91.解碼方法)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(91.解碼方法),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C++ OpenCV實(shí)戰(zhàn)之形狀識(shí)別

    C++ OpenCV實(shí)戰(zhàn)之形狀識(shí)別

    本案例通過使用OpenCV中的approxPolyDP進(jìn)行多邊形近似,進(jìn)而進(jìn)行基礎(chǔ)形狀識(shí)別(圓、三角形、矩形、星形…),快跟隨小編一起動(dòng)手嘗試一下
    2022-07-07
  • C++封裝紅黑樹以實(shí)現(xiàn)map和set方法詳解

    C++封裝紅黑樹以實(shí)現(xiàn)map和set方法詳解

    這篇文章主要介紹了C++封裝紅黑樹以實(shí)現(xiàn)map和set的方法,本章從紅黑樹核心原理入手,封裝底層紅黑樹結(jié)構(gòu),復(fù)用實(shí)現(xiàn)map與set,掌握STL關(guān)聯(lián)容器底層實(shí)現(xiàn)與C++封裝思想
    2026-05-05
  • C++二叉搜索樹及其實(shí)現(xiàn)方法實(shí)例代碼

    C++二叉搜索樹及其實(shí)現(xiàn)方法實(shí)例代碼

    這篇文章主要介紹了C++二叉搜索樹及其實(shí)現(xiàn)方法的相關(guān)資料,搜索二叉樹是一種左小右大的二叉樹結(jié)構(gòu),支持高效查找、插入和刪除操作,需要的朋友可以參考下
    2025-06-06

最新評(píng)論

西昌市| 白银市| 融水| 巴楚县| 大洼县| 沧源| 永泰县| 石林| 余庆县| 泊头市| 温泉县| 庆元县| 思茅市| 霍城县| 碌曲县| 四子王旗| 桦南县| 获嘉县| 长子县| 英超| 星子县| 安阳县| 太保市| 信宜市| 芒康县| 定西市| 赫章县| 自治县| 威信县| 兰州市| 元谋县| 蒙自县| 武穴市| 南昌县| 镇宁| 蓝田县| 巴中市| 汕头市| 长泰县| 台中县| 芦山县|