如果一個類中什么成員都沒有,簡稱為空類。
空類中真的什么都沒有嗎?并不是,任何類在什么都不寫時,編譯器會自動生成以下6個默認成員函數。
默認成員函數:用戶沒有顯式實現,編譯器會生成的成員函數稱為默認成員函數。
一.構造函數
?構造函數(Constructor)
?構造函數是 C++ 中用于初始化對象的特殊成員函數,它在對象創建時自動調用。以下是構造函數的全面解析:
1. 基本概念
?
- 作用:初始化對象成員變量
- 特點:
- 函數名與類名相同
- 無返回值(連 ?void? 都沒有)
- 支持重載(一個類可以有多個構造函數)
- 調用時機:
- 對象定義時(?Class obj;?)
- 動態創建對象時(?new Class()?)
- 臨時對象創建時(?Class()?)
?
2. 構造函數的類型
?
(1) 編譯器生成的默認構造函數
?
- 觸發條件:當類沒有定義任何構造函數時,編譯器自動生成
- 行為:
- 對基本類型(?int?/?float? 等)不初始化(值隨機)
- 對類類型成員調用其默認構造函數
?
class DefaultExample
{
public:
? ? // 編譯器自動生成默認構造函數
? ? int val; // 未初始化
};
?
void demo()
{
? ? DefaultExample obj; // val的值隨機
}
?
?
(2) 自定義默認構造函數(無參構造)
?
class MyClass
{
public:
? ? // 顯式定義默認構造函數
? ? MyClass() : x(0), y(0) {}?
private:
? ? int x, y;
};
?
?
(3) 全缺省構造函數
?
- 所有參數都有默認值,可以替代默認構造函數
?
class AllDefault
{
public:
? ? // 全缺省構造函數(也是默認構造函數)
? ? AllDefault(int a = 0, int b = 0) : x(a), y(b) {}
private:
? ? int x, y;
};
?
void demo()
{
? ? AllDefault obj1; // 等效于 AllDefault(0, 0)
? ? AllDefault obj2(5); // AllDefault(5, 0)
}
?
?
(4) 部分缺省構造函數
?
class PartialDefault
{
public:
? ? // 部分缺省參數
? ? PartialDefault(int a, int b = 10) : x(a), y(b) {}
private:
? ? int x, y;
};
?
void demo()
{
? ? PartialDefault obj(5); // PartialDefault(5, 10)
? ? PartialDefault obj2(1, 2);
}
?
?
?
?
3. 必須自定義構造函數的場景
?
在 C++ 中,以下情況必須手動編寫構造函數,不能依賴編譯器生成的默認構造函數:
?
?
?
_類包含引用類型成員
?
- 原因:引用必須在初始化時綁定到一個對象,且之后不能更改綁定目標。
- 解決方案:必須通過構造函數的初始化列表顯式初始化引用成員。
?
_類包含 const 常量成員
?
- 原因:const 成員必須在對象構造時初始化,且之后不可修改。
- 解決方案:必須在構造函數的初始化列表中初始化 const 成員。
?
_類需要管理動態資源
?
- 原因:默認構造函數不會自動分配堆內存、打開文件等資源。
- 典型場景:
- 成員包含指針并需要動態分配內存(如 ?int* data = new int[100]?)
- 需要初始化文件句柄、數據庫連接等外部資源
- 解決方案:在構造函數中顯式申請資源,并在析構函數中釋放。
?
_類成員需要特定初始值
?
- 原因:編譯器生成的默認構造函數不會初始化基本類型成員(如 ?int?/?float??等)。
- 解決方案:手動定義構造函數,確保成員初始化為預期值(例如初始化為 0 或空字符串)。
?
_類繼承體系中基類沒有默認構造函數
?
- 原因:如果基類只有帶參數的構造函數,派生類必須顯式調用基類構造函數。
- 解決方案:在派生類構造函數的初始化列表中調用基類的構造函數。
?
_需要禁止某些對象的構造方式
?
- 場景:
- 希望強制使用特定參數構造對象(如必須傳入 ID)
- 單例模式中需要私有化構造函數
- 解決方案:手動定義構造函數并刪除默認版本(如 ?ClassName() = delete?)
4. 初始化列表 vs 構造函數體內賦值
?
方式 特點?
初始化列表 - 在對象構造時直接初始化成員 - 必須用于引用/const成員初始化?
構造函數體內賦值 - 實際上是先默認構造再賦值 - 對非基本類型有額外性能開銷?
?
class InitDemo
{
public:
? ? // 推薦:初始化列表
? ? InitDemo(int a, std::string s) : num(a), str(s) {}
? ??
? ? // 不推薦:構造函數內賦值
? ? InitDemo(int a) {?
? ? ? ? num = a; // 先執行string的默認構造,再賦值
? ? ? ? str = "default";
? ? }
? ??
private:
? ? int num;
? ? std::string str; // 類類型成員
};
?
?
?
?
5. 特殊構造函數
?
(1) 委托構造函數(C++11)
?
class Delegating
{
public:
? ? // 主構造函數
? ? Delegating(int a, double b) : x(a), y(b) {}
? ??
? ? // 委托給主構造函數
? ? Delegating() : Delegating(0, 0.0) {}?
private:
? ? int x;
? ? double y;
};
?
?
(2) explicit 構造函數
?
- 禁止隱式類型轉換
?
class ExplicitDemo
{
public:
? ? explicit ExplicitDemo(int x) : val(x) {} // 必須顯式調用
};
?
void demo()
{
? ? // ExplicitDemo obj = 5; // 錯誤!禁止隱式轉換
? ? ExplicitDemo obj(5); // 正確
}
?
6. 構造函數的調用順序
?
1.?基類構造函數(如果存在繼承)
2.?成員變量的構造函數(按聲明順序)
3.?當前類的構造函數體
?
class Member
{
public:
? ? Member() { std::cout << "Member構造\n"; }
};
?
class Base
{
public:
? ? Base() { std::cout << "Base構造\n"; }
};
?
class Demo : public Base
{
public:
? ? Demo() : m(), Base() {?
? ? ? ? std::cout << "Demo構造\n";?
? ? }
private:
? ? Member m;
};
?
// 輸出順序:
// Base構造 → Member構造 → Demo構造
?
7. 實際應用建議
?
_優先使用初始化列表:尤其對類類型成員
_對單參數構造函數加?explicit?:避免意外類型轉換
_資源管理類遵循三五法則:如果需要自定義析構函數,通常也需要自定義拷貝構造和賦值運算符
_避免在構造函數中調用虛函數:此時虛函數機制未完全建立
?
class ResourceOwner
{
public:
? ? explicit ResourceOwner(size_t size)?
? ? ? ? : data(new int[size]), size(size) {}
? ??
? ? ~ResourceOwner() { delete[] data; }
? ??
? ? // 三五法則:需要自定義拷貝控制成員
? ? ResourceOwner(const ResourceOwner&) = delete;
? ? ResourceOwner& operator=(const ResourceOwner&) = delete;
? ??
private:
? ? int* data;
? ? size_t size;
};
?
?
二.析構函數
?析構函數是 C++ 中用于對象銷毀時自動調用的特殊成員函數,負責資源清理工作
1. 基本概念
?
- 作用:釋放對象占用的資源(內存/文件/鎖等)
- 特點:
- 函數名為 ?~類名?
- 無參數無返回值
- 不可重載(每個類只能有一個析構函數)
- 調用時機:
- 對象離開作用域時(棧對象)
- ?delete? 動態對象時(堆對象)
- 容器銷毀時(如 ?vector? 析構會調用元素析構)
?
class FileHandler
{
public:
? ? FileHandler()
? ?{
? ? file = fopen("data.txt", "r");
? ? }
? ? ~FileHandler()
? ? ? {?
? ? ? ? if(file) fclose(file); // 必須手動關閉文件
? ? ? ? std::cout << "資源已釋放\n";?
? ? ? }
private:
? ? FILE* file;
};
?
2. 必須自定義析構函數的場景
?
?
// 場景1:動態內存管理
class MemoryPool
{
public:
? ? MemoryPool(size_t size) { data = new int[size]; }
? ? ~MemoryPool() { delete[] data; } // 必須自定義
private:
? ? int* data;
};
?
// 場景2:文件資源管理
class DatabaseConn
{
public:
? ? ~DatabaseConn() { disconnect(); } // 確保連接關閉
};
?
// 場景3:多態基類
class Base
{
public:
? ? virtual ~Base() = default; // 虛析構函數!!!
};
?
class Derived : public Base
{
? ? ~Derived() override { /* 清理派生類資源 */ }
};
?
?
?
?
3. 默認析構函數的問題
?
問題1:淺拷貝導致雙重釋放
?
class ShallowCopy
{
public:
? ? ShallowCopy(int size) { data = new int[size]; }
? ? ~ShallowCopy() { delete[] data; } // 危險!
? ??
private:
? ? int* data;
};
?
void demo()
{
? ? ShallowCopy obj1(10);
? ? ShallowCopy obj2 = obj1; // 淺拷貝:兩個對象共享data指針
? ? // 析構時會導致同一內存被釋放兩次 → 程序崩潰
}
?
?
解決方案:實現拷貝控制(拷貝構造+賦值運算符)或禁用拷貝(C++11)
?
class SafeCopy
{
public:
? ? // 方法1:禁用拷貝
? ? SafeCopy(const SafeCopy&) = delete;
? ? SafeCopy& operator=(const SafeCopy&) = delete;
? ??
? ? // 方法2:實現深拷貝
? ? SafeCopy(const SafeCopy& other)
? ?{
? ? ? ? data = new int[other.size];
? ? ? ? std::copy(other.data, other.data+other.size, data);
? ? }
};
?
?
問題2:部分資源泄漏
?
class ResourceLeak
? {
public:
? ? ResourceLeak()
? ?{?
? ? ? ? res1 = new int[100];?
? ? ? ? res2 = new int[200];?
? ? }
? ? ~ResourceLeak() { delete[] res1; } // res2泄漏!
private:
? ? int *res1, *res2;
};
?
?
解決方案:使用 RAII 對象管理資源(如 ?std::unique_ptr?)
?
class SafeResource
{
? ? std::unique_ptr<int[]> res1;
? ? std::unique_ptr<int[]> res2;
public:
? ? SafeResource() :?
? ? ? ? res1(std::make_unique<int[]>(100)),
? ? ? ? res2(std::make_unique<int[]>(200)) {}
? ? // 無需顯式析構函數!
};
?
?
?
?
4. 析構函數調用順序
?
1.?執行析構函數體:當前類的清理代碼
2.?調用成員變量的析構函數(按聲明逆序)
3.?調用基類析構函數(若存在繼承,從派生類向基類析構)
?
class Member
{
public:
? ? ~Member() { std::cout << "Member析構\n"; }
};
?
class Base
{
public:
? ? virtual ~Base() { std::cout << "Base析構\n"; }
};
?
class Derived : public Base
{
public:
? ? ~Derived() override
? ?{
? ?std::cout << "Derived析構\n";
? ?}
private:
? ? Member m;
};
?
// 調用示例:
Base* obj = new Derived();
delete obj;?
?輸出順序:
? ?Derived析構 → Member析構 → Base析構
?
?
5. 最佳實踐
?
1.?RAII原則:資源獲取即初始化,通過對象生命周期管理資源
2.?三五法則:若自定義析構函數,通常需要同時處理拷貝/移動操作
3.?多態基類聲明虛析構函數:防止通過基類指針刪除派生類對象時資源泄漏
4.?優先使用智能指針:避免手動內存管理錯誤
?
// 現代C++推薦寫法
class ModernExample
{
? ? std::unique_ptr<int[]> data; // 自動管理內存
? ? std::mutex mtx; // RAII管理鎖
public:
? ? ModernExample() : data(std::make_unique<int[]>(100)) {}
? ? // 無需顯式析構函數!
};
關鍵總結
?
- 必須自定義析構函數:當類管理動態分配的資源或需要確保資源釋放時
- 危險陷阱:默認淺拷貝會導致雙重釋放,需通過深拷貝或禁用拷貝避免
- 多態必備:基類必須聲明虛析構函數,否則通過基類指針刪除派生類對象會導致派生類資源泄漏
- 現代替代方案:優先使用智能指針和STL容器替代裸指針/手動資源管理
三.拷貝構造函數
?
拷貝構造函數是C++中的一種特殊構造函數,用于創建一個新對象作為現有對象的副本。
?
基本語法
?
class MyClass
{
public:
? ? // 拷貝構造函數
? ? MyClass(const MyClass& other)
? ?{
? ? ? ? // 復制other的成員到當前對象
? ? }
};
?
?
特點
?
1.?參數是對同類型對象的const引用
2.?通常不應修改源對象,因此參數通常是const
3.?如果沒有顯式定義,編譯器會自動生成一個默認的拷貝構造函數
?
使用場景
?
拷貝構造函數在以下情況下被調用:
?
1.?用一個對象初始化另一個對象時:
MyClass obj1;
MyClass obj2 = obj1; // 調用拷貝構造函數
MyClass obj3(obj1); ?// 調用拷貝構造函數
?
2.?對象作為函數參數按值傳遞時:
void func(MyClass obj);
MyClass original;
func(original); // 調用拷貝構造函數
?
3.?函數返回對象時(可能被編譯器優化):
MyClass createObject()
{
? ? MyClass obj;
? ? return obj; // 可能調用拷貝構造函數
}
?
?
深拷貝與淺拷貝
?
1.?淺拷貝:僅復制指針值,不復制指針指向的內容(默認拷貝構造函數的行為)
2.?深拷貝:復制指針指向的內容,需要自定義拷貝構造函數
?
class DeepCopyExample
{
? ? int* data;
public:
? ? // 深拷貝構造函數
? ? DeepCopyExample(const DeepCopyExample& other)
? ? {
? ? ? ? data = new int(*other.data); // 分配新內存并復制值
? ? }
};
?
?
禁用拷貝
?
可以通過將拷貝構造函數聲明為delete來禁止拷貝:
?
class NonCopyable
{
public:
? ? NonCopyable(const NonCopyable&) = delete;
};
?
?
最佳實踐
?
1.?對于包含動態分配資源的類,應實現自定義拷貝構造函數
2.?對于不應被拷貝的類,應顯式禁用拷貝構造函數
3.?考慮使用移動語義(C++11引入)來提高性能
?
四.賦值運算符重載 (Copy Assignment Operator)
?
賦值運算符重載是C++中允許類自定義對象間賦值行為的特性,通常與拷貝構造函數一起使用。
?
基本語法
?
class MyClass
{
public:
? ? // 賦值運算符重載
? ? MyClass& operator=(const MyClass& other) {
? ? ? ? if (this != &other)
? ? ? ?{ // 防止自賦值
? ? ? ? ? ? // 復制other的成員到當前對象
? ? ? ? }
? ? ? ? return *this; // 支持鏈式賦值
? ? }
};
?
?
特點
?
1.?必須是成員函數,不能是友元函數
2.?通常返回當前對象的引用以支持鏈式賦值
3.?參數是對同類型對象的const引用
4.?需要處理自賦值情況
?
使用場景
?
當對象被賦值時自動調用:
?
MyClass obj1, obj2;
obj1 = obj2; // 調用賦值運算符重載
?
拷貝構造函數與拷貝賦值運算符的區別
?
拷貝構造函數和拷貝賦值運算符雖然都用于對象拷貝,但在本質上有重要區別:
?
1. 根本目的不同
?
- 拷貝構造函數:用于在創建新對象時,用已有對象初始化它。這是對象的"出生"過程。
- 拷貝賦值運算符:用于兩個已存在對象之間的賦值操作。這是對象的"改變"過程。
?
2. 調用時機不同
?
拷貝構造函數在以下情況調用:
?
MyClass obj1; ? ? ? ? ? ? // 默認構造
MyClass obj2(obj1); ? ? ? // 直接調用拷貝構造
MyClass obj3 = obj1; ? ? ?// 這也是調用拷貝構造
MyClass func(MyClass o); ?// 參數傳遞時可能調用拷貝構造
?
?
拷貝賦值運算符在以下情況調用:
?
MyClass obj1, obj2; ?// 默認構造
obj1 = obj2; ? ? ? ? // 調用賦值運算符
?
?
3. 對象生命周期不同
?
- 拷貝構造:目標對象尚未存在,需要在構造過程中初始化
- 拷貝賦值:目標對象已經存在,需要先清理原有資源再賦新值
?
4. 實現要點不同
?
拷貝構造的實現:
?
MyClass(const MyClass& other)
{
? ? // 只需關心從other復制到新對象
? ? data = new int(*other.data); // 深拷貝示例
}
?
?
拷貝賦值的實現:
?
MyClass& operator=(const MyClass& other)
{
? ? if(this != &other)
? ? { ?// 1. 檢查自賦值
? ? ? ? delete data; ? ? ?// 2. 釋放原有資源
? ? ? ? data = new int(*other.data); // 3. 深拷貝
? ? }
? ? return *this; ? ? ? ?// 4. 返回引用
}
?
?
5. 資源管理差異
?
- 拷貝構造:只需分配新資源并復制
- 拷貝賦值:需要先釋放舊資源,再分配新資源,還要處理自賦值情況
?
6. 返回值不同
?
- 拷貝構造:沒有返回值
- 拷貝賦值:通常返回對象引用以支持鏈式賦值(a = b = c)
?
實際應用建議
?
1.?遵循"三大法則":如果實現了其中任何一個(拷貝構造、拷貝賦值或析構),通常需要實現全部三個
2.?對于資源管理類,必須同時正確實現兩者
3.?現代C++中可考慮使用"復制交換慣用法"簡化實現
4.?不需要拷貝時應顯式禁用(=delete)
?
深拷貝實現示例
?
class DeepCopyExample
{
? ? int* data;
public:
? ? // 賦值運算符重載
? ? DeepCopyExample& operator=(const DeepCopyExample& other)
{
? ? ? ? if (this != &other)
? ? ? ?{
? ? ? ? ? ? delete data; // 釋放原有資源
? ? ? ? ? ? data = new int(*other.data); // 分配新內存并復制
? ? ? ? }
? ? ? ? return *this;
? ? }
};
?
?
復制交換慣用法 (Copy-and-Swap Idiom)
?
更安全和高效的實現方式:
?
class MyClass
{
? ? // ... 其他成員 ...
public:
? ? friend void swap(MyClass& first, MyClass& second) noexcept
? ? ?{
? ? ? ? // 交換所有成員
? ? ? ? using std::swap;
? ? ? ? swap(first.data, second.data);
? ? }
?
? ? MyClass& operator=(MyClass other) noexcept
? ? {
? ? ? ? swap(*this, other); // 交換當前對象與參數
? ? ? ? return *this; // other離開作用域時會自動清理舊資源
? ? }
};
?
?
禁用賦值
?
可以通過聲明為delete來禁止賦值:
?
class NonAssignable
{
public:
? ? NonAssignable& operator=(const NonAssignable&) = delete;
};
五.const成員函數
?
const成員函數是C++中保證函數不會修改對象狀態的重要機制,它在類設計中扮演著關鍵角色。
?
基本概念
?
定義方式
?
在成員函數聲明和定義的參數列表后添加?const?關鍵字:
?
class MyClass
{
public:
? ? // 聲明const成員函數
? ? void display() const;
? ??
? ? int getValue() const
? ? {
? ? ? ? return value; ?// 直接定義的const成員函數
? ? }
private:
? ? int value;
};
// 類外定義const成員函數
void MyClass::display() const
{
? ? // 函數實現
}
?
?
核心特性
?
1.?不可修改性:不能修改類的非mutable成員變量
2.?調用權限:可以被const和非const對象調用
3.?重載機制:可以與同名非const成員函數形成重載
?
使用場景
?
1. 訪問器方法(Getters)
?
class Student
{
? ? std::string name;
public:
? ? // const成員函數作為getter
? ? const std::string& getName() const
? ? ?{
? ? ? ? ?return name;
? ? ?}
};
?
?
2. 不修改對象狀態的工具函數
?
class Matrix
{
? ? double data[10][10];
public:
? ? // 計算行列式但不修改矩陣內容
? ? double determinant() const;
};
?
?
3. const對象接口
?
const Student s("Alice");
s.getName(); ?// 只能調用const成員函數
?
?
重要規則
?
1. 修改限制
?
class Counter
{
? ? int count;
? ? mutable int accessCount; ?// mutable例外
public:
? ? void increment() const
? ?{
? ? ? ? // count++; ?// 錯誤!不能修改非mutable成員
? ? ? ? accessCount++; ?// 正確:mutable成員可修改
? ? }
};
?
?
2. 重載決議
?
class TextBlock
? ?{
? ? std::string text;
public:
? ? // const版本
? ? const char& operator[](size_t pos) const
? ? {
? ? ? ? return text[pos];
? ? }
? ??
? ? // 非const版本
? ? char& operator[](size_t pos)
? ? {
? ? ? ? return text[pos];
? ? }
};
const TextBlock ctb("Hello");
TextBlock tb("World");
ctb[0]; ?// 調用const版本
tb[0]; ? // 調用非const版本
?
?
高級用法
?
1. 邏輯常量性
?
class CachedData
{
? ? mutable std::string cachedValue;
? ? bool cacheValid;
public:
? ? std::string getValue() const
? {
? ? ? ? if (!cacheValid)
? ? ? ?{
? ? ? ? ? ? // 雖然修改了成員,但不影響邏輯常量性
? ? ? ? ? ? cachedValue = computeValue();
? ? ? ? ? ? cacheValid = true;
? ? ? ? }
? ? ? ? return cachedValue;
? ? }
};
?
?
2. 返回類型優化
?
class BigData
{
? ? std::vector<int> data;
public:
? ? // 返回const引用避免拷貝
? ? const std::vector<int>& getData() const
? ? {
? ? ? ?return data;
? ? ? }
};
?
?
最佳實踐
?
1.?80%原則:80%的成員函數應該聲明為const
2.?const一致:相關函數應提供const和非const版本
3.?避免cast:不要用const_cast去掉const性質
4.?mutable慎用:只在真正不影響邏輯狀態時使用
5.?文檔說明:對mutable成員的使用添加注釋說明
?
C++中的const成員
?
const成員是指被?const?關鍵字修飾的類成員,包括const成員變量和const成員函數。
?
一、const成員變量
?
1. 特點
?
- 必須在構造函數的初始化列表中初始化
?
- 初始化后值不可修改
?
- 每個對象的const成員可以有不同的值
?
2. 聲明與初始化
?
cpp
class MyClass
{
public:
? ? const int size; // const成員變量聲明
? ??
? ? // 必須在初始化列表中初始化
? ? MyClass(int s) : size(s) {}??
? ??
? ? // 錯誤!不能在構造函數體內賦值
? ? // MyClass(int s) { size = s; }??
};
?
?
3. 使用場景
?
- 類中需要保持不變的配置參數
?
- 對象特有的常量值
?
- 防止意外修改的重要成員
?
二、const成員函數
?
1. 特點
?
- 在函數聲明和定義后加?const?關鍵字
?
- 不能修改類的任何非mutable成員變量
?
- 可以訪問但不能修改對象狀態
?
- 可以被const和非const對象調用
?
2. 語法
?
cpp
class MyClass
{
? ? int value;
public:
? ? // const成員函數
? ? int getValue() const
? ?{??
? ? ? ? return value; // 可以讀取但不能修改成員
? ? }
? ??
? ? // 錯誤!const成員函數不能修改成員
? ? // void setValue(int v) const { value = v; }
};
?
?
3. 使用場景
?
- 不修改對象狀態的getter方法
?
- 保證線程安全的成員函數
?
- 需要被const對象調用的方法
?
三、const對象
?
1. 特點
?
- 聲明為const的對象
?
- 只能調用const成員函數
?
- 所有成員變量被視為const
?
cpp
const MyClass obj(10);
int x = obj.getValue(); // 正確
// obj.setValue(20); // 錯誤!const對象不能調用非const方法
?
?
四、mutable成員
?
1. 特點
?
- 用?mutable?關鍵字修飾的成員變量
?
- 可以在const成員函數中被修改
?
- 通常用于不影響對象邏輯狀態的成員
?
2. 示例
?
cpp
class MyClass
{
? ? mutable int cache; // mutable成員
? ? int value;
public:
? ? int getValue() const
? ? {
? ? ? ? cache++; // 可以修改mutable成員
? ? ? ? return value;
? ? }
};
?
?
五、最佳實踐
?
1.?盡可能將不修改對象狀態的成員函數聲明為const
?
2.?const成員變量應在初始化列表中初始化
?
3.?對于需要頻繁修改的緩存或計數器使用mutable
?
4.?設計類時應考慮const正確性
?
5.?const成員函數可以重載非const版本
?
六.取地址及const取地址操作符重載
?
在C++中,我們可以重載取地址運算符(?operator&?)來定制對象地址獲取行為,這在某些特殊場景下非常有用。
?
基本語法
?
普通取地址運算符重載
?
class MyClass
{
public:
? ? MyClass* operator&()
? ? {
? ? ? ? return this; // 默認行為,通常不需要重載
? ? ? ? // 可以返回其他指針,但需謹慎
? ? }
};
?
?
const版本取地址運算符重載
?
class MyClass
{
public:
? ? const MyClass* operator&() const
? ?{
? ? ? ? return this; // const對象的取地址
? ? }
};
?
?
使用場景
?
1. 禁止獲取真實地址(封裝實現)
?
class NoAddress
{
private:
? ? static int dummy;
public:
? ? int* operator&() { return &dummy; } // 返回假地址
? ? const int* operator&() const
? ? ?{
? ? ? ?return &dummy;
? ? ?}
};
int NoAddress::dummy = 0;
?
?
2. 智能指針模擬
?
template<typename T>
class PtrWrapper
{
? ? T* ptr;
public:
? ? PtrWrapper(T* p) : ptr(p) {}
? ? T* operator&() { return ptr; }
? ? const T* operator&() const { return ptr; }
};
?
?
3. 代理模式
?
class AddressProxy
{
? ? int* realTarget;
public:
? ? AddressProxy(int* target) : realTarget(target) {}
? ? int** operator&() { return &realTarget; } // 返回指針的地址
? ? const int* const* operator&() const
? ? ?{ return &realTarget; }
};
?
?
重要注意事項
?
1.?謹慎重載:除非有充分理由,否則不要重載取地址運算符
2.?保持一致性:const和非const版本應保持邏輯一致
3.?避免混淆:重載行為應與常規預期相符,避免造成困惑
4.?STL兼容性:某些STL實現可能依賴標準取地址行為
?
實際應用示例
?
安全指針包裝器
?
class SafePointer
{
? ? void* ptr;
public:
? ? SafePointer(void* p) : ptr(p) {}
? ??
? ? // 普通版本
? ? void** operator&()
? ? {
? ? ? ? static int guard;
? ? ? ? if(!ptr) return reinterpret_cast<void**>(&guard);
? ? ? ? return &ptr;
? ? }
? ??
? ? // const版本
? ? const void* const* operator&() const
? ? {
? ? ? ? return const_cast<const void* const*>(&ptr);
? ? }
};
?
?
最佳實踐
?
1.?僅在特殊需求時重載取地址運算符
2.?確保重載后的行為有明確文檔說明
3.?考慮提供?addressof()?成員函數作為替代方案
4.?保持const和非const版本的邏輯一致性
5.?注意線程安全性問題
?