C++類和對象詳解(2);初識類的默認成員函數

1.類的默認成員函數

默認成員函數就是用戶沒有顯示實現,編譯器會自動生成的成員函數稱為默認成員函數。一個類我們不寫的情況下編譯器會默認生成以下的6個默認成員函數。

(1)構造函數:主要完成初始化的工作

(2)析構函數:主要完成銷毀清理的工作

(3)拷貝構造函數:使用同類對象初始化創建對象

(4)賦值重載:主要是把一個對象賦值給另一個對象

(5)普通對象取地址重載:主要用于特殊情況,如安全敏感的應用、自定義內存管理或調試目的。在大多數日常編程中,不需要重載這個運算符。

(6)const對象取地址重載:和普通對象取地址重載一樣。日常編程中不需要重載這個運算符。

以上六種默認成員函數我們主要學習前四種。

1.1構造函數

構造函數是特殊的成員函數,構造函數雖然名稱是構造,但是構造函數的主要任務是實例化時初始化對象,并不是開空間創造對象(我們常使用的局部對象是棧幀創建時,空間就開好了)。

構造函數的語法特點如下

(1)函數名可以和類名相同

class MyClass {
public:MyClass() { // 構造函數與類名相同// 初始化代碼}
};

(2)無返回值,無返回類型。甚至不能使用void(C++規定是這樣,不用糾結)

(3)對象實例化時系統會自動調用對應的構造函數

MyClass obj; // 構造函數自動調用

(4)構造函數可以重載

一個類可以有多個構造函數,只要它們的參數列表不同。

class MyClass {
public:MyClass() { // 默認構造函數// 初始化代碼}MyClass(int value) { // 帶參數的構造函數// 使用value初始化}MyClass(int a, double b) { // 另一個重載// 使用a和b初始化}
};

(5)可以有默認參數

class MyClass {
public:MyClass(int a = 0, double b = 0.0) {// 使用默認參數或提供的值初始化}
};

(6)如果類中沒有顯式定義構造函數,則C++編譯器會自動生成?個無參的默認構造函數,?旦用戶顯式定義編譯器將不再生成。

#include<iostream>
using namespace std;
class Stack {
public:Stack()//無參的默認構造函數{}
private:int* _date;int _top;int _capacity;
};
int main()
{Stack s1;return 0;
}

這里我們來調試觀察s1

會發現s1會被編譯器自動生成默認構造函數

1.2析構函數

析構函數與構造函數功能相反,析構函數不是完成對對象本身的銷毀,比如局部對象是存在棧幀的,函數結束棧幀銷毀,他就釋放了,不需要我們管,C++規定對象在銷毀時會自動調用析構函數,完成對象中資源的清理釋放工作。

析構函數的特點如下:

(1)在類名前加~

class MyClass {
public:~MyClass() { // 析構函數// 清理代碼}
};

(2)無參數,無返回值也,不能被重載。一個類只能有一個析構函數?(這里跟構造類似,也不需要加void)

class MyClass {
public:~MyClass() { // 正確// 清理代碼}// ~MyClass(int x) { } // 錯誤:析構函數不能有參數
};

(3)當對象離開其作用域或被顯示刪除時,析構函數會自動調用。這里跟構造函數類似,我們不寫編譯器自動生0成的析構函數對內置類型成員不做處理,自定類型成員會調用他的析構函數。

{MyClass obj; // 對象創建// 使用對象...
} // 對象離開作用域,析構函數自動調用MyClass* ptr = new MyClass();
delete ptr; // 析構函數被調用

(4)還需要注意的是我們顯示寫析構函數,對于自定義類型成員也會調用他的析構,也就是說自定義類型成員無論什么情況都會自動調用析構函數。

(5)對于繼承層次中的對象,析構函數的調用順序與構造函數相反:派生類的析構函數 成員對象的析構函數(按聲明逆序)基類的析構函數

class Base {
public:~Base() { std::cout << "Base destructor" << std::endl; }
};class Member {
public:~Member() { std::cout << "Member destructor" << std::endl; }
};class Derived : public Base {
private:Member member;
public:~Derived() { std::cout << "Derived destructor" << std::endl; }
};// 創建Derived對象然后銷毀
// 輸出順序:
// Derived destructor
// Member destructor
// Base destructor

1.3拷貝構造函數

拷貝構造是一種特殊的構造函數,用于創建一個新對象來當已存在對象的副本。以下是拷貝構造的主要特點。

(1)函數名與類名相同,沒有返回類型。參數必須是同類型對象的引用,通常為const引用。如果不引用可能會出現無限遞歸的現象。

class MyClass {
public:// 正確的拷貝構造函數MyClass(const MyClass& other) {// 復制邏輯}// 錯誤的拷貝構造函數示例// MyClass(MyClass other) { } // 錯誤:值傳遞會導致無限遞歸// MyClass(MyClass* other) { } // 錯誤:這不是拷貝構造函數
};

如果不用引用導致遞歸,編譯器會報錯

(2)C++規定自定義類型對象進行拷貝行為必須調用拷貝構造,所以自定義類型傳值傳參和傳值返回都會調用拷貝構造完成。

MyClass obj1;
MyClass obj2 = obj1;  // 調用拷貝構造函數
MyClass obj3(obj1);   // 調用拷貝構造函數void func(MyClass param); // 值傳遞參數時調用拷貝構造函數
func(obj1);              // 調用拷貝構造函數MyClass func2() {MyClass local;return local;       // 可能調用拷貝構造函數(取決于編譯器優化)
}

(3)若未顯式定義拷貝構造,編譯器會生成自動生成拷貝構造函數。自動生成的拷貝構造對內置類型成員變量會完成值拷貝/淺拷貝(?個字節?個字節的拷貝),對自定義類型成員變量會調用他的拷貝構造。

默認拷貝構造函數的陷阱。淺拷貝構造

這對于簡單類足夠使用,但是遇到指針類型就會出現雙重釋放的問題

class ShallowCopyExample {
private:int* data;public:ShallowCopyExample(int value) {data = new int(value);}// 默認拷貝構造函數執行淺拷貝:// ShallowCopyExample(const ShallowCopyExample& other) : data(other.data) {}~ShallowCopyExample() {delete data;}
};// 使用示例
int main()
{ShallowCopyExample obj1(42);ShallowCopyExample obj2 = obj1; // 淺拷貝:兩個對象的data指向同一內存return 0;
}
// 問題:obj2析構時會釋放內存,然后obj1析構時再次釋放同一內存 → 雙重釋放錯誤!

因為淺拷貝構造的指針指向了同一塊內存,我們用編譯器調試或者轉到反匯編就可以清楚的看到

那么我們遇到帶有指針的成員變量,就需要用自定義拷貝構造來實現深拷貝。

#include <iostream>
#include <cstdlib>  // 包含 malloc 和 free
#include <cstring>  // 包含 strcpyclass StringWithMalloc
{
private:char* data;size_t length;public:// 構造函數 - 使用 malloc 分配內存StringWithMalloc(const char* str = ""){length = strlen(str);data = (char*)malloc(length + 1);  // 使用 malloc 分配內存if (data != nullptr) {strcpy(data, str);}else {length = 0;}std::cout << "構造函數: " << (data ? data : "NULL")<< " (地址: " << (void*)data << ")" << std::endl;}// 自定義拷貝構造函數 - 使用 malloc 實現深拷貝StringWithMalloc(const StringWithMalloc& other){length = other.length;data = (char*)malloc(length + 1);  // 使用 malloc 分配新內存if (data != nullptr) {strcpy(data, other.data);}else {length = 0;}std::cout << "拷貝構造函數: " << (data ? data : "NULL")<< " (新地址: " << (void*)data << ")" << std::endl;}// 析構函數 - 使用 free 釋放內存~StringWithMalloc(){std::cout << "析構函數: " << (data ? data : "NULL")<< " (地址: " << (void*)data << ")" << std::endl;free(data);  // 使用 free 釋放內存data = nullptr;  // 防止懸空指針}
};

(4)傳值返回會產生?個臨時對象調用拷貝構造,傳值引用返回,返回的是返回對象的別名(引用),沒有產生拷貝。但是如果返回對象是?個當前函數局部域的局部對象,函數結束就銷毀了,那么使用引用返回是有問題的,這時的引用相當于?個野引用,類似?個野指針?樣。傳引用返回可以減少拷貝,但是?定要確保返回對象,在當前函數結束后還在,才能用引用返。

1.4賦值運算符重載

1.4.1運算符重載
(1)當運算符被用于類類型的對象時,C++允許我們通過運算符重載的形式指定新的含義。C++規定類類型對象使用運算符時,必須轉換成調用對應運算符重載,若沒有對應的運算符重載,則會編譯報錯。
Class Data//定義一個類
{//......
}
int main()
{Data d1;Data d2;d1-d2//這里編譯器沒有對應的運算符重載會報錯return 0;
}

(2)運算符重載是具有特殊名字的函數,他的名字是由operator和后面要定義的運算符共同構成。和其他函數?樣,它也具有其返回類型和參數列表以及函數體。

基本語法如下

返回類型 operator操作符(參數列表) {// 函數體
}

(3)重載運算符函數的參數個數和該運算符作用的運算對象數量?樣多。?元運算符有?個參數,?元運算符有兩個參數,?元運算符的左側運算對象傳給第?個參數,右側運算對象傳給第?個參數。

(4)如果?個重載運算符函數是成員函數,則它的第?個運算對象默認傳給隱式的this指針,因此運算符重載作為成員函數時,參數比運算對象少?個。

(5)運算符重載以后,其優先級和結合性與對應的內置類型運算符保持?致。

(6)不能通過連接語法中沒有的符號來創建新的操作符:比如operator@。

(7).*? ?::? ? sizeof? ? ?:? ? . 注意以上5個運算符不能重載。

(8)重載操作符至少有?個類類型參數,不能通過運算符重載改變內置類型對象的含義

int operator+(int x, int y)//里面沒有自定義的類,都是內置類型不可重載
1.4.2賦值運算符重載的特點
(1)賦值運算符重載是?個運算符重載,規定必須重載為成員函數。賦值運算重載的參數建議寫成 const 當前類類型引用,否則會傳值傳參會有拷貝。
(2)有返回值,且建議寫成當前類類型引用,引用返回可以提?效率,有返回值目的是為了支持連續賦值場景。
(3)通常需要避免淺拷貝問題,特別是當類中包含指針成員時
(4)應返回對象的引用(*this),以支持連續賦值(如 a = b = c)
(5)一般需要檢查自賦值情況(避免自身賦值導致的問題)

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

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

相關文章

PLC通信 Tpc客戶端Socket

1.PLC通信 namespace _2.PLC通信 {public partial class Form1 : Form{public Form1(){InitializeComponent();}//連接//1.型號: 跟PLC溝通 使用哪個型號的PLC//2.IP 同上//3.機臺號:同上//4.插槽號:同上Plc plc new Plc(CpuType.S71200, "192.168.25.80", 0, 1);pr…

Android 開發實戰:從零到一集成 espeak-ng 實現中文離線 TTS(無需賬號開箱即用)

簡介 在移動應用開發中,語音合成(TTS)技術是提升用戶體驗的重要工具。然而,許多開發者在集成 TTS 時面臨依賴網絡、需注冊賬號、功能受限等問題。本文將帶你從零開始,通過開源項目 espeak-ng,實現無需賬號、開箱即用的中文離線語音播報。 文章將覆蓋以下核心內容: esp…

直播APP集成美顏SDK詳解:智能美妝功能的開發實戰

在這個“顏值即正義”的時代&#xff0c;用戶對直播APP的第一印象&#xff0c;往往來自主播的畫面質量。高清的視頻固然重要&#xff0c;但如果缺少自然美顏和智能美妝功能&#xff0c;觀眾體驗就會大打折扣。于是&#xff0c;美顏SDK成了直播行業的“標配”。今天&#xff0c;…

C++內存管理:new與delete的深層解析

1. 引言在C的世界里&#xff0c;動態內存管理是一個核心話題。對于從C語言過渡到C的開發者來說&#xff0c;一個常見的困惑是&#xff1a;既然C語言的malloc和free依然可以在C中使用&#xff0c;為什么C還要引入new和delete這兩個操作符&#xff1f;本文將深入探討這兩對內存管…

【AI開發】【前后端全棧】[特殊字符] AI 時代的快速開發思維

&#x1f680; AI 時代的快速開發思維 —— 以 Django Vue3 為例的前后端分離快捷開發流程 一、AI 時代的開發新思路 在 AI 的加持下&#xff0c;軟件開發不再是“純體力活”&#xff0c;而是 思維工具自動化 的協作。 過去&#xff1a;需求 → 設計 → 開發 → 測試 → 上…

Day24_【深度學習(3)—PyTorch使用—張量的創建和類型轉換】

一、創建張量1.張量基本創建方式torch.tensor 根據指定數據創建張量 &#xff08;最重要&#xff09;torch.Tensor 根據形狀創建張量, 其也可用來創建指定數據的張量torch.IntTensor、torch.FloatTensor、torch.DoubleTensor 創建指定類型的張量1.1 torch.tensor# 方式一&…

3-12〔OSCP ? 研記〕? WEB應用攻擊?利用XSS提權

鄭重聲明&#xff1a; 本文所有安全知識與技術&#xff0c;僅用于探討、研究及學習&#xff0c;嚴禁用于違反國家法律法規的非法活動。對于因不當使用相關內容造成的任何損失或法律責任&#xff0c;本人不承擔任何責任。 如需轉載&#xff0c;請注明出處且不得用于商業盈利。 …

AI 大模型賦能智慧礦山:從政策到落地的全棧解決方案

礦山行業作為能源與工業原料的核心供給端&#xff0c;長期面臨 “安全生產壓力大、人工效率低、技術落地難” 等痛點。隨著 AI 大模型與工業互聯網技術的深度融合&#xff0c;智慧礦山已從 “政策引導” 邁入 “規模化落地” 階段。本文基于 AI 大模型智慧礦山行業解決方案&…

Node.js 項目依賴包管理

h5打開以查看 一、核心理念&#xff1a;從“能用就行”到“精細化管理” 一個規范的依賴管理體系的目標是&#xff1a; 可復現&#xff1a;在任何機器、任何時間都能安裝完全一致的依賴&#xff0c;保證構建結果一致。 清晰可控&#xff1a;明確知道每個依賴為何存在&#x…

洛谷P1835素數密度 詳解

題目如下&#xff1a;這里面有部分代碼比較有意思&#xff1a;1&#xff0c;為何開始先遍歷&#xff0c;最終值小于50000&#xff1f;因為題目要求的右邊與左邊差小于 10^6 &#xff0c;所以最多有10^3個素數&#xff0c;所以保存里面的素數數量大于1000&#xff0c;而50000的化…

突破限制:FileCodeBox遠程文件分享新體驗

文章目錄【視頻教程】1.Docker部署2.簡單使用演示3. 安裝cpolar內網穿透4. 配置公網地址5. 配置固定公網地址在隱私日益重要的今天&#xff0c;FileCodeBox與cpolar的協同為文件傳輸提供了安全高效的解決方案。通過消除公網IP限制和隱私顧慮&#xff0c;讓每個人都能掌控自己的…

以太網鏈路聚合實驗

一、實驗目的掌握使用手動模式配置鏈路聚合的方法掌握使用靜態 LACP 模式配置鏈路聚合的方法掌握控制靜態 LACP 模式下活動鏈路的方法掌握靜態 LACP 的部分特性的配置二、實驗環境安裝有eNSP模擬器的PC一臺&#xff0c;要求PC能聯網。三、實驗拓撲LSW1與LSW2均為S3700交換機。L…

autMan安裝教程

一、安裝命令 如果你系統沒安裝docker&#xff0c;請看往期教程 以下為通用命令 docker run -d --name autman --restart always -p 8080:8080 -p 8081:8081 -v /root/autman:/autMan --log-opt max-size10m --log-opt max-file3 hdbjlizhe/autman:latest解釋一下以上命令&…

【無人機】自檢arming參數調整選項

檢查項目 (英文名)中文含義檢查內容四旋翼建議 (新手 → 老手)理由說明All所有檢查啟用下面所有的檢查項目。? 強烈建議勾選這是最安全的設置&#xff0c;確保所有關鍵系統正常。Barometer氣壓計檢查氣壓計是否健康、數據是否穩定。? 必須勾選用于定高模式&#xff0c;數據異…

數字圖像處理(1)OpenCV C++ Opencv Python顯示圖像和視頻

Open CV C顯示圖像#include <iostream> #include <opencv2/opencv.hpp> using namespace cv;//包含cv命名空間 int main() {//imread(path)&#xff1a;從給定路徑讀取一張圖片&#xff0c;儲存為Mat變量對象Mat img imread("images/love.jpg");//named…

【芯片設計-信號完整性 SI 學習 1.2.2 -- 時序裕量(Margin)】

文章目錄1. 什么是時序裕量&#xff08;Margin&#xff09;1. 背景&#xff1a;為什么需要數字接口時序分析2. 時鐘周期方程3. Setup 裕量 (tMARGIN_SETUP)4. Hold 裕量 (tMARGIN_HOLD)5. 設計注意事項6. 實際應用場景2. 時序裕量的來源3. 測試方法(1) 眼圖測試 (Eye Diagram)(…

AOP 切面日志詳細

在業務方法上打注解package com.lib.service;Service public class BookService {LogExecution(description "查詢圖書")public Book query(int id) {return repo.findById(id);}LogExecution(description "借閱圖書")public void borrow(int id) {// 模…

使用paddlepaddle-Gpu庫時的一個小bug!

起初安裝的是 paddlepaddle 2.6.1版本。 用的是Taskflow的快速分詞以及ner快速識別&#xff1a;???????seg_accurate Taskflow("word_segmentation", mode"fast") ner Taskflow("ner", mode"fast")但是使用不了Gpu。想使用Gp…

量子能量泵:一種基于并聯電池與電容陣的動態直接升壓架構

量子能量泵&#xff1a;一種基于并聯電池與電容陣的動態直接升壓架構 摘要 本文提出了一種革命性的高效電源解決方案&#xff0c;通過創新性地采用并聯電池組與串聯高壓電容陣相結合的架構&#xff0c;徹底解決了低電壓、大功率應用中的升壓效率瓶頸與電池一致性難題。該方案摒…

【Linux網絡】網絡基礎概念——帶你打開網絡的大門

1. 計算機網絡背景 文章目錄1. 計算機網絡背景網絡發展2. 初識協議2.1 協議分層軟件分層的好處2.2 OSI七層模型2.3 TCP/IP五層(或四層)模型網絡發展 獨立模式 獨立模式是計算機網絡發展的最初階段&#xff0c;主要特點如下&#xff1a; 單機工作環境&#xff1a; 每臺計算機完…