C++六大默認(rèn)成員函數(shù)的實(shí)現(xiàn)
C++中的六大默認(rèn)成員函數(shù)是編譯器在特定條件下自動(dòng)生成的成員函數(shù),用于管理對(duì)象的生命周期和資源操作。它們分別是:
默認(rèn)構(gòu)造函數(shù)
作用:初始化對(duì)象,當(dāng)類沒有顯式定義任何構(gòu)造函數(shù)時(shí)生成。
生成條件:用戶未定義任何構(gòu)造函數(shù)。
注意:若類有其他構(gòu)造函數(shù)(如帶參數(shù)的構(gòu)造函數(shù)),需顯式使用 = default 聲明默認(rèn)構(gòu)造函數(shù)。
class Person { public: //Person() //{ //} 不寫的話默認(rèn)自動(dòng)生成 void GetAge() { std::cout << _age << std::endl; } private: int _age; }; int main() { Person p; p.GetAge(); }
其特征如下:
函數(shù)名與類名相同。
無返回值。
對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。
構(gòu)造函數(shù)可以重載。
如果類中沒有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無參的默認(rèn)構(gòu)造函數(shù),一旦用戶顯式定義編譯器將不再生成。
編譯器生成默認(rèn)的構(gòu)造函數(shù)會(huì)對(duì)自定類型成員調(diào)用的它的默認(rèn)成員
函數(shù)。C++11 中針對(duì)內(nèi)置類型成員不初始化的缺陷,又打了補(bǔ)丁,即:內(nèi)置類型成員變量在類中聲明時(shí)可以給默認(rèn)值。
默認(rèn)析構(gòu)函數(shù)
作用:釋放對(duì)象資源,默認(rèn)析構(gòu)函數(shù)調(diào)用成員變量的析構(gòu)函數(shù)。
生成條件:用戶未定義析構(gòu)函數(shù)。
注意:若類管理動(dòng)態(tài)資源(如堆內(nèi)存),需自定義析構(gòu)函數(shù)以避免內(nèi)存泄漏
class Person { public: //Person() //{ //} 不寫的話默認(rèn)自動(dòng)生成 void GetAge() { std::cout << _age << std::endl; } ~Person() { } private: int _age; }; int main() { Person p; p.GetAge(); }
RAII技術(shù)
RAII(Resource Acquisition Is Initialization,資源獲取即初始化)是C++中一種管理資源的編程技術(shù)。它通過將資源的生命周期與對(duì)象的生命周期綁定在一起,利用C++的構(gòu)造函數(shù)和析構(gòu)函數(shù)來自動(dòng)管理資源,從而避免了手動(dòng)分配和釋放資源可能帶來的問題,如內(nèi)存泄漏、資源未正確釋放等。
RAII的核心思想
- 資源在對(duì)象構(gòu)造時(shí)獲取:當(dāng)一個(gè)對(duì)象被創(chuàng)建時(shí),它的構(gòu)造函數(shù)負(fù)責(zé)獲取所需的資源(例如,動(dòng)態(tài)內(nèi)存分配、文件打開、網(wǎng)絡(luò)連接等)。
- 資源在對(duì)象銷毀時(shí)釋放:當(dāng)對(duì)象離開作用域或被顯式刪除時(shí),其析構(gòu)函數(shù)會(huì)自動(dòng)釋放之前獲取的資源。
優(yōu)點(diǎn)
- 異常安全性:由于資源管理由構(gòu)造和析構(gòu)函數(shù)自動(dòng)處理,即使程序中拋出了異常,也能確保資源得到正確釋放。
- 簡化代碼:開發(fā)者不需要手動(dòng)跟蹤每個(gè)資源的狀態(tài),并且可以在不使用顯式的
try-finally
塊的情況下保證資源的釋放。 - 防止資源泄露:只要對(duì)象被正確地創(chuàng)建并最終銷毀,資源就會(huì)被正確釋放。
示例
以下是一個(gè)簡單的例子,展示了如何使用RAII來管理動(dòng)態(tài)分配的內(nèi)存:
#include <iostream> class ResourceHandler { private: int* data; public: // 構(gòu)造函數(shù):資源獲取 ResourceHandler() { data = new int(10); // 分配資源 std::cout << "Resource acquired." << std::endl; } // 析構(gòu)函數(shù):資源釋放 ~ResourceHandler() { delete data; // 釋放資源 std::cout << "Resource released." << std::endl; } void showData() const { std::cout << "Data: " << *data << std::endl; } }; void useResource() { ResourceHandler handler; handler.showData(); // 不需要手動(dòng)釋放資源,handler離開作用域時(shí)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù) } int main() { useResource(); return 0; }
在這個(gè)例子中,ResourceHandler
類負(fù)責(zé)管理一個(gè)整數(shù)類型的動(dòng)態(tài)分配內(nèi)存。構(gòu)造函數(shù)在對(duì)象創(chuàng)建時(shí)分配資源,而析構(gòu)函數(shù)在對(duì)象銷毀時(shí)釋放這些資源。這樣就確保了無論函數(shù)useResource
如何退出(正常結(jié)束或因異常退出),資源都會(huì)被正確釋放。
應(yīng)用場景
RAII不僅限于內(nèi)存管理,還可以應(yīng)用于其他資源類型,如文件句柄、網(wǎng)絡(luò)套接字、數(shù)據(jù)庫連接等。標(biāo)準(zhǔn)庫中的智能指針(如std::unique_ptr
、std::shared_ptr
)、鎖機(jī)制(如std::lock_guard
、std::unique_lock
)都是RAII原則的實(shí)際應(yīng)用案例。通過使用這些工具,可以有效地減少資源管理錯(cuò)誤,提高代碼的安全性和可靠性。
默認(rèn)拷貝構(gòu)造
聲明形式:ClassName(const ClassName&)
作用:通過已有對(duì)象初始化新對(duì)象,默認(rèn)執(zhí)行淺拷貝。
生成條件:用戶未定義拷貝構(gòu)造函數(shù)。
注意:若類包含指針或動(dòng)態(tài)資源,需自定義深拷貝防止重復(fù)釋放。
class Person { public: Person() { } Person(const Person& person) { this->_age = person._age; } ~Person() { } void GetAge() { std::cout << _age << std::endl; } private: int _age; }; int main() { Person p; p.GetAge(); }
深拷貝和淺拷貝
class Stack { public: //初始化 Stack() { _array = new int[20]; } //默認(rèn)生成拷貝構(gòu)造 //析構(gòu) ~Stack() { delete[] _array; } private: int* _array; size_t _size; size_t _capacity; }; int main() { Stack s1; Stack s2(s1); }
這里我沒有寫實(shí)際的拷貝構(gòu)造函數(shù),這里s2調(diào)用的默認(rèn)的拷貝構(gòu)造,所以s2_array的地址就是s1中_array的地址,這就叫淺拷貝:
這樣代碼就會(huì)有問題,因?yàn)橐粋€(gè)地址會(huì)被析構(gòu)兩次:
正確的方法應(yīng)該是給s2的array開辟一塊新的空間:
class Stack { public: //初始化 Stack() { _array = new int[20]; } Stack(const Stack& st) { _array = new int[10]; _size = st._size; _capacity = st._capacity; } //析構(gòu) ~Stack() { delete[] _array; } private: int* _array; size_t _size; size_t _capacity; }; int main() { Stack s1; Stack s2(s1); }
這樣的拷貝我們稱為深拷貝,再次運(yùn)行程序:
默認(rèn)拷貝賦值運(yùn)算符
聲明形式:ClassName& operator=(const ClassName&)
作用:將已有對(duì)象的值賦給另一個(gè)對(duì)象,默認(rèn)淺拷貝。
生成條件:用戶未定義拷貝賦值運(yùn)算符。
注意:需處理自賦值問題,并在資源管理時(shí)實(shí)現(xiàn)深拷貝。
class Stack { public: //初始化 Stack() { _array = new int[20]; } Stack(const Stack& st) { _array = new int[10]; _size = st._size; _capacity = st._capacity; } Stack& operator=(const Stack& st) { if (this != &st) { _array = new int[10]; _size = st._size; _capacity = st._capacity; } return *this; } //析構(gòu) ~Stack() { delete[] _array; } private: int* _array; size_t _size; size_t _capacity; }; int main() { Stack s1; Stack s2; s2 = s1; }
移動(dòng)構(gòu)造函數(shù)(C++11起)
聲明形式:ClassName(ClassName&&)
作用:通過右值引用“竊取”資源,避免深拷貝開銷。
生成條件:用戶未定義拷貝操作、移動(dòng)操作或析構(gòu)函數(shù)。
注意:移動(dòng)后源對(duì)象應(yīng)處于有效但未定義狀態(tài)(如空指針)。
class Stack { public: //初始化 Stack() { _array = new int[20]; } Stack(const Stack& st) { _array = new int[10]; _size = st._size; _capacity = st._capacity; } Stack& operator=(const Stack& st) { if (this != &st) { _array = new int[10]; _size = st._size; _capacity = st._capacity; } return *this; } void swap(Stack& st) { std::swap(_array, st._array); std::swap(_size, st._size); std::swap(_capacity, st._capacity); } //移動(dòng)構(gòu)造函數(shù) Stack(Stack&& st):_array(nullptr), _size(0), _capacity(0) { swap(st); } //析構(gòu) ~Stack() { delete[] _array; } private: int* _array; size_t _size; size_t _capacity; }; int main() { Stack s1; Stack s2(std::move(s1)); }
默認(rèn)移動(dòng)賦值運(yùn)算符(C++11起)
聲明形式:ClassName& operator=(ClassName&&)
作用:通過右值引用轉(zhuǎn)移資源所有權(quán)。
生成條件:同移動(dòng)構(gòu)造函數(shù)。
注意:需正確處理自移動(dòng)賦值。
class Stack { public: //初始化 Stack() { _array = new int[20]; } Stack(const Stack& st) { _array = new int[10]; _size = st._size; _capacity = st._capacity; } Stack& operator=(const Stack& st) { if (this != &st) { _array = new int[10]; _size = st._size; _capacity = st._capacity; } return *this; } void swap(Stack& st) { std::swap(_array, st._array); std::swap(_size, st._size); std::swap(_capacity, st._capacity); } //移動(dòng)構(gòu)造函數(shù) Stack(Stack&& st):_array(nullptr), _size(0), _capacity(0) { swap(st); } //移動(dòng)復(fù)制構(gòu)造 Stack& operator=(Stack&& st) { swap(st); st._array = nullptr; st._size = 0; st._capacity = 0; return *this; } //析構(gòu) ~Stack() { delete[] _array; } private: int* _array; size_t _size; size_t _capacity; }; int main() { Stack s1; Stack s2; s2 = std::move(s1); }
在C++中,前置++
和后置++
運(yùn)算符可以通過成員函數(shù)或非成員函數(shù)的形式進(jìn)行重載。兩者的主要區(qū)別在于參數(shù)列表和返回值:
- 前置
++
:增加對(duì)象的值,并返回增加后的對(duì)象引用。 - 后置
++
:首先保存當(dāng)前對(duì)象的狀態(tài),然后增加對(duì)象的值,最后返回之前保存的對(duì)象的副本。
取地址及const取地址操作符重載
在C++中,取地址操作符(&
)和常量取地址操作符(const &
)通常不需要顯式地重載,因?yàn)榫幾g器提供了默認(rèn)的實(shí)現(xiàn),它們分別返回對(duì)象或常量對(duì)象的內(nèi)存地址。然而,在某些特定情況下,你可能想要自定義這些操作符的行為。
取地址操作符重載
當(dāng)你重載取地址操作符時(shí),你通常是為了改變其默認(rèn)行為,例如返回一個(gè)代理對(duì)象的地址而不是原始對(duì)象的地址。不過這種情況非常少見,大多數(shù)時(shí)候并不需要這樣做。
常量取地址操作符重載
類似地,重載常量版本的取地址操作符也是為了提供特定的行為,但同樣,這并不是常見的需求。
示例代碼
盡管不常見,這里還是給出如何重載這兩種操作符的基本示例:
#include <iostream> class MyClass { private: int value; public: MyClass(int val) : value(val) {} // 重載取地址操作符 int* operator&() { std::cout << "非const取地址操作符被調(diào)用" << std::endl; return &value; } // 重載const取地址操作符 const int* operator&() const { std::cout << "const取地址操作符被調(diào)用" << std::endl; return &value; } }; int main() { MyClass obj(10); const MyClass constObj(20); int* addr = &obj; // 調(diào)用非const版本 const int* constAddr = &constObj; // 調(diào)用const版本 std::cout << "*addr: " << *addr << std::endl; std::cout << "*constAddr: " << *constAddr << std::endl; return 0; }
輸出結(jié)果
非const取地址操作符被調(diào)用
const取地址操作符被調(diào)用
*addr: 10
*constAddr: 20
在這個(gè)例子中,我們?yōu)?code>MyClass類重載了取地址操作符和常量取地址操作符。當(dāng)通過非常量對(duì)象調(diào)用operator&()
時(shí),會(huì)調(diào)用非常量版本的操作符,并打印一條消息。而當(dāng)通過常量對(duì)象調(diào)用operator&()
時(shí),則調(diào)用常量版本的操作符,并打印另一條不同的消息。
注意事項(xiàng)
- 謹(jǐn)慎使用:一般情況下,不需要也不建議重載這兩個(gè)操作符,除非有特別的需求。這是因?yàn)樗鼈兏淖兞藰?biāo)準(zhǔn)語義,可能會(huì)導(dǎo)致混淆或者不可預(yù)期的行為。
- 保持一致性:如果你決定重載這些操作符,請確保它們的行為符合邏輯且一致,避免引入錯(cuò)誤。
- 理解限制:需要注意的是,即使你重載了取地址操作符,也無法阻止使用內(nèi)置的取地址操作來獲取對(duì)象的實(shí)際地址。例如,
&obj
總是可以獲得obj
的實(shí)際地址,除非你完全隱藏了對(duì)象的訪問方式。
總的來說,重載取地址操作符和常量取地址操作符是一種高級(jí)技巧,適用于特定場景下的特殊需求。在大多數(shù)日常編程任務(wù)中,這種重載是不必要的。
擴(kuò)展:前置++和后置++重載
重載規(guī)則
- 前置
++
只需要一個(gè)參數(shù)(即調(diào)用該運(yùn)算符的對(duì)象本身),并且通常返回一個(gè)指向修改后的對(duì)象的引用。 - 后置
++
需要兩個(gè)參數(shù):第一個(gè)是調(diào)用該運(yùn)算符的對(duì)象本身,第二個(gè)是一個(gè)int類型的占位參數(shù),用于區(qū)分前置和后置形式。后置++
返回的是操作前對(duì)象的一個(gè)副本(通常是通過值返回)。
#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; class Count { public: //重載后置++ Count operator++() { ++_count; return *this; } //后置++ Count operator++(int) { Count temp = *this; ++_count; return temp; } int getCount() const { return _count; } private: int _count = 0; }; int main() { Count myCounter; std::cout << "Initial count: " << myCounter.getCount() << std::endl; ++myCounter; // 調(diào)用前置++ std::cout << "After prefix increment: " << myCounter.getCount() << std::endl; Count d; d = myCounter++; // 調(diào)用后置++ std::cout << "After postfix increment: " << d.getCount() << std::endl; return 0; }
到此這篇關(guān)于C++六大默認(rèn)成員函數(shù)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ 默認(rèn)成員函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++深入探究類與對(duì)象之對(duì)象模型與this指針使用方法
C++對(duì)象模型中只有類的非static成員以及一個(gè)指向虛函數(shù)表的指針被配置于類對(duì)象內(nèi),其他都在類對(duì)象外,在 C++ 中,每一個(gè)對(duì)象都能通過 this 指針來訪問自己的地址。this 指針是所有成員函數(shù)的隱含參數(shù)。因此,在成員函數(shù)內(nèi)部,它可以用來指向調(diào)用對(duì)象2022-04-04VS報(bào)錯(cuò)C1189及MSB3721解決方法
在使用VS進(jìn)行CUDA編譯時(shí)出現(xiàn)錯(cuò)誤,本文主要介紹了VS報(bào)錯(cuò)C1189及MSB3721解決方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06C語言實(shí)現(xiàn)定時(shí)器控制LED燈閃爍
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)定時(shí)器控制LED燈閃爍,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05c++ 結(jié)構(gòu)體內(nèi)存對(duì)齊基本概念及示例
這篇文章主要介紹了c++ 結(jié)構(gòu)體內(nèi)存對(duì)齊基本概念及示例,幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下2020-12-12