析構函數詳解

目錄

  • 析構函數
    • 概念
    • 特性
    • 對象的銷毀順序

感謝各位大佬對我的支持,如果我的文章對你有用,歡迎點擊以下鏈接
🐒🐒🐒 個人主頁
🥸🥸🥸 C語言
🐿?🐿?🐿? C語言例題
🐣🐣🐣 python
🐓🐓🐓 數據結構C語言
🐔🐔🐔 C++
🐿?🐿?🐿? 文章鏈接目錄

析構函數

概念

通過上一篇文章我們知道一個對象是怎么來的,那一個對象又是怎么沒呢的?

析構函數:與構造函數功能相反,析構函數不是完成對對象本身的銷毀,局部對象銷毀工作是由編譯器完成的。

而對象在銷毀時會自動調用析構函數,完成對象中資源的清理工作(有點像數據結構中的Destroy函數,作用就是清理鏈表 樹 或堆上的空間清理,如果不清理會出現內存泄漏的情況)

注意對象空間的開辟和銷毀不需要我們去解決,這些都是由系統去完成的(全局 對象 靜態都是系統自己去解決),而像堆上的空間就需要我們去完成了,比如malloc開辟空間的時候需要我們去完成,在最后釋放的時候也是需要我們自己去free掉空間

特性

析構函數是特殊的成員函數,其特征如下:

  1. 析構函數名是在類名前加上字符 ~
  2. 無參數無返回值類型。
    具體結構如下:
class Date
{~Date(){;}
};

類名前的~在C語言中表示按位與取反,這里的取反有完全相反的意思,所以 ~放在析構函數這里就是想說明析構函數的作用和構造函數是完全不同的

特別注意析構函數是沒有參數的,而構造函數是有參數的,因為構造函數要構造,傳參可以初始化,而析構函數完全沒必要傳參,所以就沒有參數

  1. 一個類只能有一個析構函數。若未顯式定義,系統會自動生成默認的析構函數。注意:析構函數不能重載
class Date
{
public:Date(){_year = 1;}~Date(){cout << "~Date()" << endl;}
private:int _year;
};
int main()
{Date d1;return 0;
}

在這里插入圖片描述

  1. 對象生命周期結束時,C++編譯系統系統自動調用析構函數。

  2. 關于編譯器自動生成的析構函數,是否會完成一些事情呢?下面的程序我們會看到,編譯器生成的默認析構函數,對自定類型成員調用它的析構函數。

  3. 如果類中沒有申請資源時,析構函數可以不寫,直接使用編譯器生成的默認析構函數
    比如Date類;有資源申請時,一定要寫,否則會造成資源泄漏,比如Stack類。

由于析構函數和構造函數都是特殊的類,所以都是有this指針的

class Date
{
public:Date(){_year = 1;}~Date(){cout << this << endl;cout << "~Date()" << endl;}void Print(){cout << this << endl;cout << "Print()" << endl;}
private:int _year;
};
void func()
{Date d2;
}
int main()
{func();Date d1;d1.Print();return 0;
}

通過調試我們可以看到,d1和d2的地址以this指針的方式傳給函數,d1和d2在生命周期結束時會調用析構函數,而析構函數里面是打印this指針
在這里插入圖片描述
我們來看看下面的代碼來具體理解析構函數

typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申請空間失敗");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();擴容_array[_size] = data;_size++;}//~Stack()//{//	if (_array)//	{//		free(_array);//		_array = NULL;//		_capacity = 0;//		_size = 0;//	}//}
private:DataType* _array;int _capacity;int _size;
};
int main()
{Stack s;s.Push(1);s.Push(2);
}

在C語言中當沒有調用Destroy函數會發生內存泄漏,具體過程如下

main函數會在棧上開辟一塊空間,這塊空間中也包含Stack s的指針DataType* _array(只是這個指針在mian函數開辟的空間里)
在這里插入圖片描述
DataType* _array中_arrray的作用是保存Stack s開辟空間的地址
在main函數執行完后,會將main函數在棧上開辟的空間都銷毀,其中就包括了指針_array
由于_array是保存著Stack s開辟空間的地址,最終會因為指針_array被銷毀,導致找不到Stack s開辟出的空間

在這里插入圖片描述
所以沒調用Destroy函數發生的后果是很嚴重的,并且我們經常會忘記調用Destroy函數,為了解決這個問題才有了析構函數,因為析構函數自動調用,并且編譯器可以自動生成析構函數,這對我們來說是非常方便的

但是需要注意的是默認生成的析構函數和默認生成的構造函數類似,對內置類型不做處理,自定義類型的成員會去調用他的析構函數

對象的銷毀順序

生命周期對于現在學到的來說有兩種,一種是局部(存在一些函數中,因為調用函數會開辟棧幀,所以函數結束后棧幀也會被銷毀,函數中的局部變量也就銷毀了),另一種是靜態或者全局的(存在靜態區里,在mian函數結束后就會銷毀)

而對象生命周期結束時,C++編譯系統系統自動調用析構函數,那如果有多個對象生命周期同時結束,系統會優先給誰調用析構函數

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(1);Date d2(2);
}

這段代碼中我們只定義了一個成員變量_year,其他的_month以及_day都只是聲明,不占用內存空間,mian函數中Date d1(1),Date d2(2)是對_year進行初始化,在函數結束后兩個對象的生命周期都會結束,而銷毀的順序如圖
在這里插入圖片描述
這個調用的順序像棧中的后進先出,Date d1先入棧,所以最后調用析構函數,事實上對象確實存儲在棧上的,因為類其實是一個函數,在函數調用時會建立棧幀,所以空間存儲在棧上

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(1);Date d2(2);static Date d3(3);
}

那如果讓Date d3加上一個static去修飾結果會怎么樣
在這里插入圖片描述
加上static修飾后Date d3的存儲區域就發生變化了,d3存儲在一個單獨的靜態區中,雖然d3是一個局部變量,但是他的生命周期在經過static修飾后變成全局,所以d3會在main函數結束后銷毀,而在main函數結束前會將里面的d1和d2等局部變量先銷毀掉,所以d3排在最后

我們再來看看下面的代碼,這段代碼中定義了一個函數func,將類的對象定義在函數中,其中d4是被static修飾的,而d3沒有被修飾,然后在main函數中調用func

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
void func()
{Date d3(3);static Date d4(4);
}
int main()
{Date d1(1);Date d2(2);func();
}

銷毀順序是3 2 1 4,具體原因還是因為func也是一個函數,空間開辟在棧上的,滿足后進先出原則,所以先銷毀對象d3(對于d3為什么是最先銷毀,可能是因為他在函數func中,算是一個局部中的局部吧),然后又是d2 d1,d4因為被static修飾,所以最后銷毀
在這里插入圖片描述
我們再在main函數外定義一個對象d5又會怎么樣

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
void func()
{Date d3(3);static Date d4(4);
}
Date d5(5);
int main()
{Date d1(1);Date d2(2);func();
}

結果是3 2 1 4 5
在這里插入圖片描述
d5雖然沒有被static修飾,但是他定義在main函數外的,所以他自己就是一個全局變量,但是這里的全局變量有兩個,一個是d4,一個是d5,他們的銷毀順序是否也和自己的位置有關呢?

我們將d5的定義移到func函數上邊,發現沒有變化,所以推測可能是因為d4是在func函數中,所以相對于d5來講,d4的聲明周期是局部的
在這里插入圖片描述

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
static Date d6(6);
Date d5(5);
void func()
{Date d3(3);static Date d4(4);
}
int main()
{func();Date d1(1);Date d2(2);

我們再在main函數外定義一個d6,用static修飾他的順序又會怎么樣
在這里插入圖片描述
當d6和d5交換順序后,發現銷毀的順序變化了,所以我們得出結論,全局的銷毀順序和局部的銷毀順序也是一樣的,當d5后入棧時,d5就先銷毀,而static修飾全局變量d6,并不會改變d6的銷毀順序
在這里插入圖片描述
在這里插入圖片描述
如果在多個函數func中定義類的順序會怎么樣

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
static Date d6(6);
Date d5(5);
void func2()
{Date d7(7);static Date d8(8);
}
void func1()
{Date d3(3);static Date d4(4);
}int main()
{Date d1(1);Date d2(2);func1();func2();
}

在這里插入圖片描述
這里說下我的想法,func1和func2因為都是在棧上開的空間,所以他們的銷毀的順序滿足后進先出,具體判斷誰先銷毀的方法就是看誰最先被調用,也就是在main函數中去看func1是否比func2先調用,如果先比func2調用,那就說明func1先開辟空間,所以func1要比func2后銷毀
在這里插入圖片描述
最終順序總結如下
局部對象(后定義先析構)->局部靜態->全局對象(后定義先析構)

類中沒有顯示定義析構函數,系統則會自動生成默認的析構函數,那這個析構函數是否和構造函數一樣基本上什么事都不做呢?
由于自定義類型的盡頭是內置類型,對應類而言如果類中沒有申請資源時,析構函數可以不寫(因為不寫不會有影響),有資源申請時,一定要寫,否則會造成資源泄漏

為什么析構不可以自己去處理內置類型呢?
因為內置類型中有指針等許多不能隨便處理的類型,假如指針指向了一塊空間,如果析構函數可以處理內置類型的話,有可能會直接把指針指向的空間給銷毀了,這樣指針就變成了野指針

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

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

相關文章

yolov8實戰之 .pt 轉. tensorRT

1 yolo 訓練 1.1修改自己的數據集合 我是有3個類別&#xff0c;差不多這么些數據 1.2 訓練 from ultralytics import YOLO # Load a model model YOLO("yolov8m.yaml") # build a new model from scratch #model YOLO(E:/pythonCode/pythonProject1/runs/detec…

風電功率預測 | 基于PSO-BP神經網絡實現風電功率預測(附matlab完整源碼)

風電功率預測 風電功率預測完整代碼風電功率預測 基于粒子群優化算法(Particle Swarm Optimization, PSO)的BP神經網絡是一種常見的方法,用于實現風電功率預測。下面是一個基于PSO-BP神經網絡實現風電功率預測的一般步驟: 數據準備:收集與風電場發電功率相關的數據,包括…

農林科學SCI期刊,IF=6+,影響力高,對國人非常友好!

一、期刊名稱 Crop Journal 二、期刊簡介概況 期刊類型&#xff1a;SCI 學科領域&#xff1a;農林科學 影響因子&#xff1a;6.6 中科院分區&#xff1a;1區 出版方式&#xff1a;開放出版 版面費&#xff1a;$900 三、期刊征稿范圍 《作物雜志》是一份雙月刊、國際、同…

PHP使用Browsershot進行網頁截圖

Browsershot是什么 Spatie Browsershot 是一個開源PHP庫&#xff0c;它允許開發者在PHP應用程序中生成網頁的截圖。 這個庫特別適用于Laravel框架&#xff0c;但也可以在其他 PHP 應用程序中使用。 主要特點 無頭瀏覽器截圖&#xff1a;使用無頭版本的 Chrome 或 Chromium 瀏…

整理好了!2024年最常見 100 道 Java基礎面試題(四十九)

上一篇地址&#xff1a;整理好了&#xff01;2024年最常見 100 道 Java基礎面試題&#xff08;四十八&#xff09;-CSDN博客 九十七、Class.forName 和 ClassLoader 的區別&#xff1f; Class.forName 和 ClassLoader 是Java中用于加載類的兩個不同的概念&#xff0c;它們在類…

10W 3KVAC隔離 寬電壓輸入 AC/DC 電源模塊 ——TP10AF系列

TP10AF系列輸出功率為10W&#xff0c;具有可靠性高、更小的體積、性價比高等特點&#xff0c;廣泛用于工控和電力儀器、儀表、智能家居等相關行業。

SMB攻擊利用之-mimikatz上傳/下載流量數據包逆向分析

SMB協議作為windows環境下最為常見的一種協議,在歷史上出現過無數的通過SMB協議進行網絡攻擊利用的案例,包括針對SMB協議本身以及通過SMB協議實施網絡攻擊。 本文將介紹一種通過SMB協議的常見利用方式,即向遠程主機傳輸mimikatz,作為我的專欄《SMB攻擊流量數據包分析》中的…

Oracle數據塊之數據行中的SCN

從Oracle 10g開始&#xff0c;如果在表級別打開ROW DEPENDENCIES&#xff0c;業務數據行發生更改時會在數據塊中進行登記。 可以通過DUMP數據塊來觀察上述SCN&#xff1a; &#xff08;1&#xff09;創建測試表&#xff0c;插入3條測試數據&#xff0c;插入一條提交一次。并調用…

解析建筑裝飾乙級資質標準及申請流程

建筑裝飾乙級資質標準 資歷與信譽 必須具備獨立的企業法人資格。社會信譽良好&#xff0c;注冊資本不少于100萬元人民幣。 技術條件 專業技術人員配備齊全、合理&#xff0c;滿足相應資質標準中對主要專業技術人員數量和專業的具體要求。通常包括但不限于室內設計、建筑、環境藝…

jar包增量更新分析

jdk自帶工具jdeps&#xff0c;可分析class依賴關系&#xff08;依賴的其它類和jar&#xff09;。 團隊&#xff0c;可以在此工具結果的基礎上再詳細分析對比出增量文件&#xff1b; 思路如下&#xff1a; jdeps分別分析出舊包和新包的文件依賴關系。并對比出新增的文件列表、…

前端學習第一課

AJAX 事先說明&#xff0c;這只是記錄&#xff0c;并不是從零到一的教學內容&#xff0c;如果想要學習的話&#xff0c;可以跳過本文章了 ok&#xff0c;轉回正題&#xff0c;正如上面所說&#xff0c;這只是記錄。其實我是有一定的前端基礎的&#xff0c;也做過涉及相關的開發…

【工具】macOS、window11訪問limux共享目錄\共享磁盤,samba服務安裝使用

一、samba服務安裝 Samba是一個免費的開源軟件實現&#xff0c;使得非Windows操作系統能夠與Windows系統進行文件和打印服務共享。它實現了SMB/CIFS協議&#xff0c;并且能夠在Linux、Unix、BSD等多種系統上運行。 安裝 samba&#xff1a; sudo yum install samba配置 samba…

【kali工具】NMAP 高級使用技巧

NMAP 高級使用技巧 6.1.3 NMAP 語法及示例 語法&#xff1a;nmap [Scan Type(s)] [Options] 例 1&#xff1a;使用 nmap 掃描一臺服務器 默認情況下&#xff0c;Nmap 會掃描 1000 個最有可能開放的 TCP 端口。 ┌──(root&#x1f480;xuegod53)-[~] └─# nmap 192.168…

【介紹下Python多線程,什么是Python多線程】

&#x1f308;個人主頁: 程序員不想敲代碼啊 &#x1f3c6;CSDN優質創作者&#xff0c;CSDN實力新星&#xff0c;CSDN博客專家 &#x1f44d;點贊?評論?收藏 &#x1f91d;希望本文對您有所裨益&#xff0c;如有不足之處&#xff0c;歡迎在評論區提出指正&#xff0c;讓我們共…

【氣象常用】時間序列的線性擬合

效果圖&#xff1a; 主要步驟&#xff1a; 1. 數據準備&#xff1a;下載Hadley Centre observations datasets的HadSST數據 可參考【氣象常用】時間序列圖-CSDN博客 2. 數據處理&#xff1a;計算線性擬合 3. 圖像繪制&#xff1a;繪制折線及擬合線&#xff0c;并添加文本 …

Nacos部署選擇數據源mysql8.0,啟動報錯No DataSource Set(終極解決方案)

Nacos部署選擇數據源mysql8.0&#xff0c;啟動報錯No DataSource Set&#xff08;終極解決方案&#xff09; 選擇mysql5.7正常&#xff0c;但是選擇mysql8.0就報這個錯&#xff0c;配置都確認無問題&#xff0c;但就是用不了mysql8.0 排查了好久&#xff0c;發現是數據庫字符集…

其他自動化工程師都在偷偷學習AI技術,你再不學就落后了!一篇文章教會你使用AI!

其他自動化工程師都在偷偷學習AI技術&#xff0c;你再不學就落后了&#xff01;一篇文章教會你使用AI&#xff01; 哈嘍&#xff0c;大家好&#xff0c;我是小叔。了解小叔的朋友都清楚&#xff0c;我從來都不是標題黨&#xff0c;我只會用美女圖片來吸引你們&#x1f602;&am…

python 六句話讓電腦告訴你,剛才插入的串口編號

六句話讓電腦告訴你&#xff0c;我的串口號 第一步&#xff0c;安裝python 編譯器以及pyserial 模塊第二步&#xff0c;寫入代碼 import serial.tools.list_ports usart_list list(serial.tools.list_ports.comports()) input("Please insert your serial port:")…

【OpenCV 基礎知識 4】分離圖像通道

cvSplit()是openCV中的一個函數&#xff0c;它分別復制每個通道到多個單通道圖像。 void cvSplit( const CvArr* src, CvArr* dst0, CvArr* dst1, CvArr* dst2, CvArr* dst3 )&#xff1b;.cvSplit()函數將復制src的各個通道到圖像dst0&#xff0c;dst1&#xff0c;dst2和dst3中…

midjourney里有哪些常用參數?

一、stylize參數 Midjourney 經過訓練&#xff0c;可以生成更加具有藝術色彩、構圖和形式的圖像。 --stylize或參數--s影響該訓練的應用程度。 低風格化值生成的圖像與提示詞非常匹配&#xff0c;但藝術性較差。數值越高藝術性更好&#xff0c;但是和描述詞相關性更差&#…