C++(智能指針)

智能指針

1.基礎:

1.1 概念

智能指針是用于自動管理動態分配內存的RAII(Resource Acquisition Is Initialization)對象。它們通過自動釋放內存來防止內存泄漏,是替代裸指針的安全工具。

1.2 解析

眾所周知,堆內存對象 需要 手動 使用 delete 銷毀 如果 沒有 使用 delete 銷毀 就會 造成 內存 泄漏
所以C + + 在ISO9 8 標準 引入了 智能 指針 概念 I S O 1 1 趨于 完善

智能指針可以堆內存對象具有棧內存對象特點原理需要手動回收內內存對象套上一個棧內存的模板類對象即可

如下圖,智能指針類似把堆區指針對象放到新的指針類中進行管理,這樣就可以和棧區對象一樣,所處的{}結束就會自動釋放內存,不用手動delete。

2.分類:

自動指針:auto_ptr? ? ? ? (C++ISO98,已被廢除

唯一指針:unique_ptr? ? (C++IOS11)

共享指針:shared_ptr? ? (C++IOS11)

虛指針:weak_ptr? ? ? ? ? (C++IOS11)

2.1 自動指針

(1)使用:
#include <iostream>
#include <memory>using namespace std;class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":構造函數" << endl;}~Animal(){cout << breed << ":析構函數" << endl;}void print(){cout << "物種:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){{Animal *a = new Animal("狗","mike");auto_ptr<Animal> ap1(a);                //自動指針管理堆區指針對象aap1.get()->print();                     //使用get函數調用管理的指針對象//ap1.release();                        //直接放棄對已有指針對象a的管理,不會釋放堆區空間,如果不delete對象a可能導致內存泄露//ap1.reset();                          //放棄對已有指針對象a的管理,同時釋放堆區空間/**1. 放棄對原有指針的管理,同時釋放空間,*2. 再創建新的堆區對象*3. ap1指針對新的堆區指針對象進行管理*/ap1.reset(new Animal("貓","lisa"));ap1.get()->print();                     //打印新管理的對象的成員變量cout << "程序塊運行結束" << endl;}cout << "程序運行結束" << endl;return 0;
}

(2)問題(被廢除原因):

由于成員變量存在指針類型因此拷貝構造函數賦值運算符重載使用出現問題淺拷貝不同auto_ptr復制語義會造成資源控制權轉移問題

#include <iostream>
#include <memory>using namespace std;class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":構造函數" << endl;}~Animal(){cout << breed << ":析構函數" << endl;}void print(){cout << "物種:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){{Animal *a = new Animal("狗","mike");auto_ptr<Animal> ap1(a);                //自動指針管理堆區指針對象aauto_ptr<Animal> ap2(ap1);              //顯示調用拷貝函數cout << ap1.get() << " " << ap2.get() << endl;auto_ptr<Animal> ap3 = ap2;             //隱式調用拷貝函數cout << ap1.get() << " " << ap2.get() << " " << ap3.get() << endl;auto_ptr<Animal> ap4;ap4 = ap3;                              //調用的是重載的賦值運算符cout << ap1.get() << " " << ap2.get() << " " << ap3.get() << " " << ap4.get() << endl;cout << "程序塊運行結束" << endl;}cout << "程序運行結束" << endl;return 0;
}

2.2 唯一指針

(1)特點:

1.和auto_ptr基本一致,唯一的不同就是unique_ptr指針對資源對象有唯一控制權,不能使用常規語法直接賦值和調用拷貝構造函數拷貝資源對象的控制權。

2.如果想讓別的unique_ptr指針搶奪資源控制權就使用move(ap1)。

(2)使用:
#include <iostream>
#include <memory>using namespace std;class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":構造函數" << endl;}~Animal(){cout << breed << ":析構函數" << endl;}void print(){cout << "物種:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){{Animal *a = new Animal("狗","mike");unique_ptr<Animal> up1(a);                //自動指針管理堆區指針對象aup1.get()->print();//unique_ptr<Animal> up2(up1);                  //不使用move不能轉移控制權,下面兩種方式一樣unique_ptr<Animal> up2(move(up1));              //顯示調用拷貝函數cout << up1.get() << " " << up2.get() << endl;unique_ptr<Animal> up3 = move(up2);             //隱式調用拷貝函數cout << up1.get() << " " << up2.get() << " " << up3.get() << endl;unique_ptr<Animal> up4;up4 = move(up3);                              //調用的是重載的賦值運算符cout << up1.get() << " " << up2.get() << " " << up3.get() << " " << up4.get() << endl;cout << "程序塊運行結束" << endl;}cout << "程序運行結束" << endl;return 0;
}

2.3 共享指針

(1)特點:

1.對一個堆區對象進行管理,解決了搶奪控制權轉換的問題,可以多個共享指針一起管理一個堆區對象。

2.新增一個引用計數,對一起管理同一個對象的指針計數,每多一個共享指針管理這個堆區對象,引用計數加一。

3.當管理這個堆區空間的所有共享指針都被釋放,才會回收這個堆區對象。?

4.shared_ptr有兩種創建方式,一種和之前的創建方式一樣。第二種常用,是其他智能指針沒有的,使用:shared_ptr<類型> sp1 = make_shared<類型>(初始化內容)。

(2)使用:
#include <iostream>
#include <memory>using namespace std;class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":構造函數" << endl;}~Animal(){cout << breed << ":析構函數" << endl;}void print(){cout << "物種:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){shared_ptr<Animal> sp3;{//shared_ptr<Animal> sp1(new Animal("狗","mike"));              //先創建堆區對象,再讓智能 指針引用管理堆區對象,速度慢shared_ptr<Animal> sp1 = make_shared<Animal>("狗","mike");       //直接創建智能指針并且引用對象,一步到位sp1.get()->print();cout << sp1.get() << endl;cout << "引用計數:" << sp1.use_count() << endl;shared_ptr<Animal> sp2 = sp1;               //隱式調用拷貝構造函數cout << sp1.get() << " " << sp2.get() << endl;cout << "引用計數:" << sp1.use_count() << endl;sp3 = sp2;              //賦值運算符重載cout << sp1.get() << " " << sp2.get() << " " << sp3.get() << endl;cout << "引用計數:" << sp1.use_count() << endl;cout << "程序塊運行結束" << endl;}cout << "引用計數:" << sp3.use_count() << endl;         //程序塊結束,只剩下ap3管理對象cout << "程序運行結束" << endl;return 0;
}

2.4 虛指針

虛指針weak_ptr是用來觀查共享指針所管理的資源,像一個旁觀者,來記錄觀查的對象(堆區對象)是否被shared_ptr管理,有幾個share_ptr在管理。

(1)特點:

1.虛指針weak_ptr是一個不控制資源對象管理的智能指針,不會影響資源的引用計數,主要的目的是協助share_ptr工作

2.通過weak_ptr的構造函數,參數傳入一個持有資源對象的share_ptr或者weak_ptr的指針

3.weak_ptr與資源呈弱相關性,不能單獨對資源進行控制,不可以調用get()函數操作資源

4.weak_ptr可以調用.lock函數讓shared_ptr指針獲得一個持有資源的share_ptr,在調用.lock函數前要先檢測weak_ptr的引用計數是否大于零(觀察的資源是否還有shared_ptr指針還在管理,如果沒有,對應的資源也不存在)。

#include <iostream>
#include <memory>using namespace std;class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":構造函數" << endl;}~Animal(){cout << breed << ":析構函數" << endl;}void print(){cout << "物種:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){weak_ptr<Animal> wp2;shared_ptr<Animal> sp3;{shared_ptr<Animal> sp1 = make_shared<Animal>("狗","mike");       //直接創建智能指針并且引用對象,一步到位sp1.get()->print();                            //打印weak_ptr<Animal> wp1(sp1);                  //使用虛函數wp1來觀測共享指針對堆區對象引用次數,不會增加引用計數cout << "引用計數:" << sp1.use_count() << endl;cout << "引用計數:" << wp1.use_count() << endl;shared_ptr<Animal> sp2 = sp1;               //隱式調用拷貝構造函數//sp1.reset();                              //刪除當前共享指針,如果還有其他共享指針管理就不釋放所管理的資源wp2 = sp2;                                  //wp2也觀測堆區對象cout << "引用計數:" << sp1.use_count() << " " << wp2.use_count() << endl;sp3 = sp2;cout << "程序塊運行結束" << endl;}//即使觀測的共享指針被釋放,wp2依舊存在,wp1被釋放cout << "引用計數:" << wp2.use_count() << endl;         //程序塊結束,虛函數觀測管理資源對象的引用計數--0cout << "程序運行結束" << endl;return 0;
}

3.手寫一個共享指針Share_ptr類

  • 構造函數
  • 拷貝構造函數
  • 賦值運算符
  • get函數
  • use_count函數
  • reset函數
  • 析構函數

代碼實現:

#include <iostream>
#include <memory>using namespace std;template <class T>
class Shareptr{
private:T *res = nullptr;int *count = nullptr;
public:Shareptr(){}Shareptr(T *s):res(s),count(new int(1)){}       //構造函數Shareptr(const Shareptr &sp):res(sp.res),count(sp.count){ //拷貝構造函數(*count)++;}T* get()const{                      //調用管理的資源return res;}int use_count()const{return *count;}void reset(){                   //刪除當前共享指針,如果還有其他共享指針管理資源對象,不刪除資源對象if(count != nullptr && res != nullptr){       //如果當前共享指針不是空(*count)--;if((*count) == 0){delete res;delete count;}res = nullptr;count = nullptr;}}Shareptr &operator =(const Shareptr &sp){       //賦值運算符重載if(&sp != this){reset();res = sp.res;count = sp.count;(*count)++;}return *this;}~Shareptr(){reset();}
};class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":構造函數" << endl;}~Animal(){cout << breed << ":析構函數" << endl;}void print(){cout << "物種:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){Shareptr<Animal> sp3;{//Shareptr<Animal> sp1 = make_shared<Animal>("金漸層","lisa");//Shareptr<Animal> sp1 = new Animal("金漸層","lisa");      //隱式調用構造函數Shareptr<Animal> sp1(new Animal("金漸層","lisa"));       //顯示調用構造函數sp1.get()->print();cout << sp1.use_count() << " " << sp1.get() << endl;Shareptr<Animal> sp2 = sp1;                              //調用拷貝構造函數cout << sp1.use_count() << " " << sp1.get() << endl;cout << sp2.use_count() << " " << sp2.get() << endl;sp3 = sp1;                                               //調用重載的賦值運算符cout << sp1.use_count() << " " << sp1.get() << endl;cout << sp2.use_count() << " " << sp2.get() << endl;cout << sp3.use_count() << " " << sp3.get() << endl;}cout << "程序結束" << endl;return 0;
}

擴展:shared_ptr 可能產生的內存泄露

問題描述:

當兩個或多個對象通過?shared_ptr?互相持有對方時,會形成循環引用(也叫“環狀引用”)。這樣即使它們都不再被外部引用,由于它們內部的?shared_ptr?還在互相引用,引用計數永遠不會歸零,導致內存無法釋放,產生內存泄漏。

錯誤示例:

#include <iostream>
#include <memory>
using namespace std;class Car; // 前向聲明class Person {
public:shared_ptr<Car> car;~Person() { cout << "Person destroyed" << endl; }
};class Car {
public:shared_ptr<Person> owner;~Car() { cout << "Car destroyed" << endl; }
};int main() {auto p = make_shared<Person>();auto c = make_shared<Car>();p->car = c;c->owner = p;// main 結束后,Person 和 Car 都不會被銷毀
}

運行后不會輸出?A destroyed?和?B destroyed,因為它們的引用計數始終大于0。

解決辦法

用?weak_ptr?打破循環引用。

weak_ptr?是一種不增加引用計數的智能指針。通常在一方用?shared_ptr,另一方用?weak_ptr。

修改后:

#include <iostream>
#include <memory>
using namespace std;class Car; // 前向聲明class Person {
public:shared_ptr<Car> car;~Person() { cout << "Person destroyed" << endl; }
};class Car {
public:weak_ptr<Person> owner; // 用 weak_ptr~Car() { cout << "Car destroyed" << endl; }
};int main() {auto p = make_shared<Person>();auto c = make_shared<Car>();p->car = c;c->owner = p; // 這里是 weak_ptr,不增加引用計數// main 結束后,Person 和 Car 都會被正確銷毀
}

這樣,A?和?B?都會被正確釋放,輸出:

總結

循環引用會導致?shared_ptr?管理的對象無法釋放,造成內存泄漏。

用?weak_ptr?替代其中一方的?shared_ptr,即可打破循環,避免泄漏。

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

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

相關文章

Sentinel 授權規則詳解與自定義異常處理

Sentinel 授權規則詳解與自定義異常處理 在微服務系統中&#xff0c;權限控制和訪問保護是至關重要的一環。本文將詳細介紹如何通過 Sentinel 的 授權規則&#xff08;AuthorityRule&#xff09; 控制資源訪問權限&#xff0c;并結合實際案例說明如何設置白名單與黑名單&#…

LeetCode Hot 100 最大子數組和

給你一個整數數組 nums &#xff0c;請你找出一個具有最大和的連續子數組&#xff08;子數組最少包含一個元素&#xff09;&#xff0c;返回其最大和。 子數組是數組中的一個連續部分。 示例 1&#xff1a; 輸入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5,4] 輸出&#xff1a;6…

Python UI自動化演進格局:從傳統庫到AI驅動的智能代理

引言 UI自動化的持久需求 在現代軟件工程和業務流程管理中&#xff0c;圖形用戶界面&#xff08;GUI&#xff09;自動化扮演著至關重要的角色。它不僅僅局限于軟件測試領域&#xff0c;更是機器人流程自動化&#xff08;RPA&#xff09;、自動化數據錄入、遺留系統集成以及在AP…

【Java面試】如何解決MQ死信隊列?

如何解決MQ死信隊列&#xff1f; 一、預防死信產生&#xff08;從源頭減少死信&#xff09; 消費者端健壯性優化 捕獲所有可能的異常&#xff0c;區分可恢復異常&#xff08;如網絡超時&#xff09;和不可恢復異常&#xff08;如數據格式錯誤&#xff09;。對可恢復異常實現自…

RGB+EVS視覺融合相機:事件相機的革命性突破?

一、單一EVS事件相機的原理 事件相機&#xff08;EVS&#xff09;是一種新型的視覺傳感器&#xff0c;其設計靈感來源于生物視覺系統。與傳統相機不同&#xff0c;事件相機并不以固定的幀率捕獲整個圖像&#xff0c;而是每個像素獨立工作&#xff0c;當檢測到亮度變化超過預設…

DBeaver 設置阿里云中央倉庫地址的操作步驟

DBeaver 設置阿里云中央倉庫地址的操作步驟&#xff08;適用于解決驅動下載緩慢或失敗的問題&#xff09; 一、最新阿里云 Maven 倉庫地址 主倉庫地址&#xff08;推薦&#xff09;&#xff1a; http://maven.aliyun.com/nexus/content/groups/public/ 123 備用地址&#xff…

Qt:QCustomPlot庫的QCPAxis

在 QCustomPlot 中&#xff0c;QCPAxis 是圖表坐標系的核心組件&#xff0c;負責管理坐標軸的所有視覺和功能特性。它提供了豐富的定制選項&#xff0c;使開發者能夠創建高度專業化的數據可視化圖表。 核心功能概述 功能類別關鍵特性相關方法基本結構坐標軸位置、方向axisTyp…

七天學會SpringCloud分布式微服務——05——OpenFeign

1、OpenFeign實現遠程調用 1.1 services.pom引入依賴 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>1.2 在service-order微服務中新建feign.Product…

大語言模型的通用局限性與全球技術演進

基于行業最新數據修訂&#xff08;2025Q2&#xff09; 一、知識截止期&#xff1a;全球模型的進化差異 所有LLM都存在??知識截止期&#xff08;Knowledge Cut-off&#xff09;??&#xff0c;即模型訓練數據的時間上限。這在技術迭代飛快的軟件開發領域尤為致命——2023年后…

常見網絡安全威脅和防御措施

網絡安全威脅是一種技術風險&#xff0c;會削弱企業網絡的防御能力&#xff0c;危及專有數據、關鍵應用程序和整個 IT 基礎設施。由于企業面臨廣泛的威脅&#xff0c;因此他們應該仔細監控和緩解最關鍵的威脅和漏洞。網絡安全問題有七大類&#xff0c;它們都包括多種威脅&#…

人工智能和云計算對金融未來的影響

你有沒有想過&#xff0c;你的錢是否會由人工智能而不是銀行來管理&#xff1f;如果你的銀行不存在于真實的地方&#xff0c;而是存在于幾千公里之外的某臺大型超級計算機上&#xff0c;那會怎樣&#xff1f;這可能有一天會發生&#xff0c;讓我們看看它是如何發生的&#xff0…

Vue3——項目配置eslint+prettier

一、安裝依賴 pnpm add -D eslint prettier eslint-plugin-vue vue-eslint-parser typescript-eslint/eslint-plugin typescript-eslint/parser eslint-config-prettier eslint-plugin-prettier typescript-eslint二、創建或修改 eslint.config.cjs // eslint.config.cjs con…

人工智能編程三大核心流程詳解--機器學習、神經網絡、NLP自然語言處理

對于學習人工智能階段&#xff0c;代碼可以寫出來&#xff0c;主要是按照構建流程一步一步&#xff0c;所以本篇博客主要是通過三個大點來介紹&#xff1a;第一個點是機器學習中預測損失值與真實值之間的誤差流程&#xff1b;第二點是深度學習中神經網絡搭建流程&#xff1b;第…

《AI for Science:深度學習如何重構基礎科學的發現范式?》

前言 前些天發現了一個巨牛的人工智能免費學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到網站 AI for Science&#xff1a;深度學習如何重構基礎科學的發現范式&#xff1f; 副標題&#xff1a;從費曼圖到神經微分方程&…

創客匠人:創始人 IP 打造引領知識變現新路徑?

在當下知識經濟蓬勃發展的時代&#xff0c;知識變現已成為眾多創作者和從業者關注的焦點。創客匠人作為行業內的重要參與者&#xff0c;為創始人 IP 打造與知識變現提供了獨特且有效的思路。? 創始人 IP 打造在知識變現中占據著關鍵地位。創客匠人認為&#xff0c;一個成功的…

JVM調優實戰 Day 7:JVM線程分析與死鎖排查

【JVM調優實戰 Day 7】JVM線程分析與死鎖排查 文章標簽 jvm調優, 線程分析, 死鎖排查, JVM監控, Java性能優化, JVM參數配置 文章簡述 在Java應用的高并發場景中&#xff0c;線程管理與死鎖問題往往是性能瓶頸的根源。本文作為“JVM調優實戰”系列的第7天&#xff0c;深入解析…

Kotlin中協程掛起函數的本質

一、核心概念&#xff1a;掛起函數的本質 1. 核心定義 掛起函數&#xff08;Suspending Function&#xff09;是 Kotlin 協程的核心機制&#xff0c;它允許函數在執行過程中暫停&#xff08;掛起&#xff09;而不阻塞線程&#xff0c;并在條件滿足時恢復執行。 2. 與普通函數…

人工智能中的集成學習:從原理到實戰

大家好&#xff01;今天我們來聊聊人工智能領域中一個非常強大的技術——集成學習&#xff08;Ensemble Learning&#xff09;&#x1f60e;。——這個讓模型預測能力飆升的“團隊合作”神器&#xff01;無論你是剛入門的新手還是想復習的老司機&#xff0c;這篇通俗教程都能幫…

大事件項目記錄13-登錄優化-redis

一、redis優化登錄接口。 原有代碼中在修改密碼在產生新令牌后并未將舊的令牌主動失效&#xff0c;舊的令牌依然可以使用 &#xff0c;會產生安全隱患&#xff0c;所以需要對其進行優化。 1.令牌主動失效機制。 &#xff08;1&#xff09;登錄成功后&#xff0c;給瀏覽器響應令…

重塑音視頻敘事:Premiere文本剪輯與Podcast AI降噪的革命性工作流

一、 開篇的另一些心里話 最近淘到個好東西&#xff0c;是來自奧地利Blueskyy藝術學院的Adobe教育版授權&#xff0c;深度體驗下來&#xff0c;感覺就像是給我的創意工具箱做了一次“滿配”升級&#xff0c;有些心得不吐不快&#xff0c;必須跟同路的設計師朋友們碰一碰。 在分…