網站首頁 編程語言 正文
本文主要介紹 C++ 編程語言中賦值運算符重載函數(operator=)的相關知識,同時通過示例代碼介紹賦值運算符重載函數的使用方法。
1 概述
1.1 Why
首先介紹為什么要對賦值運算符“=”進行重載。某些情況下,當我們編寫一個類的時候,并不需要為該類重載“=”運算符,因為編譯系統為每個類提供了默認的賦值運算符“=”,使用這個默認的賦值運算符操作類對象時,該運算符會把這個類的所有數據成員都進行一次賦值操作。例如有如下類:
class A
{
public:
int a;
int b;
int c;
};
對這個類的對象進行賦值時,使用默認的賦值運算符是沒有問題的。
示例代碼內容如下:
#include <iostream>
using namespace std;
class ClassA
{
public:
int a;
int b;
int c;
};
int main()
{
ClassA obj1;
obj1.a = 1;
obj1.b = 2;
obj1.c = 3;
ClassA obj2;
obj2 = obj1;
cout << "obj2.a is: " << obj2.a << endl;
return 0;
}
編譯并執行上述代碼,結果如下:
通過上述結果能夠知道:通過使用系統默認的賦值運算符“=”,可以讓對象 obj2 中的所有數據成員的值與對象 obj1 相同。這種情況下,編譯系統提供的默認賦值運算符可以正常使用。
但是,在下面的示例中,使用編譯系統提供的默認賦值運算符,就會出現問題了。
示例代碼內容如下:
#include <iostream>
#include <string.h>
using namespace std;
class ClassA
{
public:
ClassA()
{
}
ClassA(const char* pszInputStr)
{
pszTestStr = new char[strlen(pszInputStr) + 1];
strncpy(pszTestStr, pszInputStr, strlen(pszInputStr) + 1);
}
virtual ~ClassA()
{
delete pszTestStr;
}
public:
char* pszTestStr;
};
int main()
{
ClassA obj1("liitdar");
ClassA obj2;
obj2 = obj1;
cout << "obj2.pszTestStr is: " << obj2.pszTestStr << endl;
cout << "addr(obj1.pszTestStr) is: " << &obj1.pszTestStr << endl;
cout << "addr(obj2.pszTestStr) is: " << &obj2.pszTestStr << endl;
return 0;
}
編譯并運行上述代碼,結果如下:
上述錯誤信息表明:當對象 obj1 和 obj2 進行析構時,由于重復釋放了同一塊內存空間,導致程序崩潰報錯。在這種情況下,就需要我們重載賦值運算符“=”了。
2 示例代碼
2.1 示例代碼1
我們修改一下前面出錯的示例代碼,編寫一個包含賦值運算符重載函數的類,修改后的代碼內容如下:
#include <iostream>
#include <string.h>
using namespace std;
class ClassA
{
public:
ClassA()
{
}
ClassA(const char* pszInputStr)
{
pszTestStr = new char[strlen(pszInputStr) + 1];
strncpy(pszTestStr, pszInputStr, strlen(pszInputStr) + 1);
}
virtual ~ClassA()
{
delete pszTestStr;
}
// 賦值運算符重載函數
ClassA& operator=(const ClassA& cls)
{
// 避免自賦值
if (this != &cls)
{
// 避免內存泄露
if (pszTestStr != NULL)
{
delete pszTestStr;
pszTestStr = NULL;
}
pszTestStr = new char[strlen(cls.pszTestStr) + 1];
strncpy(pszTestStr, cls.pszTestStr, strlen(cls.pszTestStr) + 1);
}
return *this;
}
public:
char* pszTestStr;
};
int main()
{
ClassA obj1("liitdar");
ClassA obj2;
obj2 = obj1;
cout << "obj2.pszTestStr is: " << obj2.pszTestStr << endl;
cout << "addr(obj1.pszTestStr) is: " << &obj1.pszTestStr << endl;
cout << "addr(obj2.pszTestStr) is: " << &obj2.pszTestStr << endl;
return 0;
}
編譯并運行上述代碼,結果如下:
通過上述結果能夠看到,利用賦值運算符重載函數,解決了對象賦值時,析構函數多次釋放同一塊內存空間的問題。
對于上述代碼,有以下幾點需要說明:
當為一個類的對象賦值(可以用本類對象為其賦值,也可以用其它類型的值為其賦值)時,該對象(如本例的 obj2)會調用該類的賦值運算符重載函數,進行具體的賦值操作。如上述代碼中的“obj2 = obj1;”語句,用 obj1 為 obj2 賦值,則會由 obj2 調用 ClassA 類的賦值運算符重載函數;
下方語句和語句“ClassA obj2 = obj1;”在調用函數上是有區別的:前者第一句是對象 obj2 的聲明及定義,調用類 ClassA 的無參構造函數,所以“obj2 = obj1;”一句是在對象 obj2 已經存在的情況下,用 obj1 來為 obj2 賦值,調用的是賦值運算符重載函數;而后者,是用 obj1 來初始化 obj2,調用的是拷貝構造函數??截悩嬙旌瘮档恼Z句樣式為“ClassA(const ClassA& cls)”,關于拷貝構造函數的詳細內容,請參考相關內容,此處不展開介紹;
ClassA obj2;
obj2 = obj1;
當程序沒有顯式地提供一個以“本類或本類的引用”為參數的賦值運算符重載函數時,編譯器會自動生成一個默認的賦值運算符重載函數(即默認賦值運算符)。
2.2 示例代碼2
示例代碼內容如下:
#include<iostream>
#include<string>
using namespace std;
class Data
{
private:
int data;
public:
// 構造函數
Data()
{
};
// 構造函數
Data(int _data):data(_data)
{
cout << "This is constructor" << endl;
}
// 賦值運算符重載函數
Data& operator=(const int _data)
{
cout << "This is operator=(int _data)" << endl;
data = _data;
return *this;
}
};
int main()
{
// 調用構造函數
Data data1(1);
Data data2, data3;
// 調用賦值運算符重載函數
data2 = 1;
// 調用默認的賦值運算符重載函數
data3 = data2;
return 0;
}
編譯并執行上述代碼,結果如下:
上述結果表明:“data2 = 1;”語句調用了我們提供的以 int 型參數(而非本類或本類的引用)為形參的賦值運算符重載函數;而“data3 = data2;”的成功執行,說明該語句調用了編譯器提供的默認的賦值運算符重載函數。
如果將上述代碼中賦值運算符重載函數去掉,重新編譯執行,結果如下:
上述結果說明,當用一個非類 A 的值(如上面的 int 類型值)為類 A 的對象賦值時:
- 如果檢測到構造函數和賦值運算符重載函數同時存在,則會優先調用賦值運算符重載函數;
- 如果只檢測到構造函數,就會調用構造函數。
3 總結
綜合本文內容,可以知道針對以下情況,需要顯式地提供賦值運算符重載函數(即自定義賦值運算符重載函數):
- 用非類 A 類型的值為類 A 的對象賦值時(當然,這種情況下我們也可以不提供相應的賦值運算符重載函數,而只提供相應的構造函數,如更改后的示例代碼2);
- 用類 A 類型的值為類 A 的對象賦值,且類 A 的數據成員中含有指針的情況下,必須顯式提供賦值運算符重載函數(如示例代碼1)。
原文鏈接:https://blog.csdn.net/liitdar/article/details/80656156
相關推薦
- 2022-05-01 WinForm中Application.Idle方法詳解_C#教程
- 2023-06-18 C#?指針內存控制Marshal內存數據存儲原理分析_C#教程
- 2022-12-01 Python?subprocess庫六個實例快速掌握_python
- 2022-05-01 skywalking容器化部署docker鏡像構建k8s從測試到可用_docker
- 2022-07-30 Linux常見命令-壓縮和解壓類 一、gzip/gunzip 壓縮 二、zip/unzip 壓縮 三
- 2023-05-05 Golang實現簡易的命令行功能_Golang
- 2022-09-19 Android自定義對話框的簡單實現_Android
- 2022-06-14 go語言中的udp協議及TCP通訊實現示例_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支