文章目錄
- 一、構造函數
- 1.1定義
- 1.2語法
- 1.3特性
- 二、析構函數
- 2.1定義
- 2.2語法
- 2.3特性
- 三、拷貝構造函數
- 3.1定義
- 3.2語法
- 3.3特性
- 3.4淺拷貝
- 3.4.1定義
- 3.4.2淺拷貝的風險
- 3.5深拷貝
一、構造函數
1.1定義
在C++中,構造函數(Constructor) 是一種特殊的成員函數,用于在創建對象時初始化其成員變量。構造函數的名稱必須與類名相同,且沒有返回類型(包括 void)。構造函數在對象創建時自動調用,確保對象處于有效狀態。
1.2語法
class ClassName {
public:ClassName(){內容} // 默認構造函數(無參)ClassName(參數列表){內容} // 帶參構造函數// ... 其他成員函數
};
示例:
class Date {
public:Date(){_year = 1970;_month = 1;_day = 1;// 默認構造函數實現。如若未定義則是隨機值。}Date(int year, int month, int day){_year = year;_month = month;_day = day;// 帶參構造函數}void print() {cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main() {Date d1; // 調用默認構造函數Date d2(2023, 5, 15); // 調用帶參構造函數Date d3{}; // C++11統一初始化方式調用默認構造函數d1.print(); // 輸出: 1970-1-1d2.print(); // 輸出: 2023-5-15d3.print(); // 輸出: 1970-1-1return 0;
}
1.3特性
構造函數的主要任務并不是開空間創建對象,而是初始化對象。 它具有以下特點:
- 1.函數名與類名相同。
- 2.無返回值。
- 3.對象實例化時編譯器自動調用對應的構造函數。
- 4.構造函數可以重載。
- 5.如果未寫構造函數,編譯器默認生成的是"類名();"
二、析構函數
2.1定義
在C++中,析構函數(Destructor) 是一種特殊的成員函數,用于在對象生命周期結束時自動執行清理工作(如釋放內存、關閉文件等)。它的名稱是在類名前加 ~,沒有返回類型,也不接受任何參數。一般在有動態內存申請的時候要寫析構函數。沒動態申請的一般不需要寫析構。
2.2語法
class ClassName {
public:~ClassName(); // 析構函數,函數體定義默認沒有,也可以自己定義。如下文示例。
};
示例:
class Date {
public:~Date(){cout << "Date對象被銷毀" << endl;}
private:int year;int month;int day;
};int main()
{Date a;return 0;
}
//程序結束后會打印"Date對象被銷毀"。
2.3特性
- 1.自動調用。
- 2.不能重載。
三、拷貝構造函數
3.1定義
拷貝構造函數是構造函數的一種重載形式,專門用于用已存在的對象創建新對象,核心是實現對象的“復制”。它的參數必須是同類對象的引用(通常加 const ,防止意外修改原對象),這是與普通構造函數的關鍵區別。
3.2語法
class MyClass {
public:// 拷貝構造函數聲明MyClass(const MyClass& other);
};// 拷貝構造函數實現
MyClass::MyClass(const MyClass& other) {// 拷貝邏輯
}
3.3特性
-
1.參數類型:
①必須是對同類對象的const引用②使用引用避免無限遞歸
③const確保不修改源對象
-
2.無返回值:構造函數沒有返回類型
-
3.通常不聲明為explicit:允許隱式拷貝構造
3.4淺拷貝
3.4.1定義
-
1.逐成員復制:簡單復制對象的所有非靜態成員變量
-
2.指針共享:對于指針成員,僅復制指針值(地址),不復制指向的內容
-
3.默認行為:編譯器生成的默認拷貝構造函數執行的就是淺拷貝
示例:
class Time
{
public:Time(){_hour = 1;_minute = 1;_second = 1;}Time(const Time& t)//這就是淺拷貝{_hour = t._hour;_minute = t._minute;_second = t._second;}
private:int _hour;int _minute;int _second;
};
3.4.2淺拷貝的風險
雙重釋放問題:
假如arr2是arr1的拷貝。
當arr1和arr2離開作用域時:
先調用arr2的析構函數釋放內存。
再調用arr1的析構函數再次釋放同一內存 → 程序崩潰
3.5深拷貝
深拷貝是指:
-
1.創建新對象時完全復制所有數據
-
2.對指針成員分配新內存并復制內容
-
3.副本與原對象完全獨立,不共享任何資源
示例:(棧)
class SafeStack {
private:int* data;int capacity;int topIndex;public:// 構造函數SafeStack(int size): capacity(size), topIndex(-1), data(new int[size]) {}// 深拷貝構造函數SafeStack(const SafeStack& other): capacity(other.capacity),topIndex(other.topIndex),data(new int[other.capacity]) {// 復制棧中所有元素for (int i = 0; i <= topIndex; ++i) {data[i] = other.data[i];}}// 深拷貝賦值運算符SafeStack& operator=(const SafeStack& other) {if (this != &other) { // 自賦值檢查// 創建臨時副本int* newData = new int[other.capacity];// 復制數據for (int i = 0; i <= other.topIndex; ++i) {newData[i] = other.data[i];}// 替換舊數據(異常安全)delete[] data;data = newData;capacity = other.capacity;topIndex = other.topIndex;}return *this;}// 移動構造函數(C++11)SafeStack(SafeStack&& other) noexcept: data(other.data),capacity(other.capacity),topIndex(other.topIndex) {other.data = nullptr;other.capacity = 0;other.topIndex = -1;}// 析構函數~SafeStack() {delete[] data;}void push(int value) {if (topIndex == capacity - 1) {throw std::overflow_error("Stack is full");}data[++topIndex] = value;}int pop() {if (topIndex == -1) {throw std::underflow_error("Stack is empty");}return data[topIndex--];}void print() {std::cout << "Stack: [ ";for (int i = 0; i <= topIndex; ++i) {std::cout << data[i] << " ";}std::cout << "]\n";}
};int main() {SafeStack s1(5);s1.push(1);s1.push(2);s1.push(3);// 測試拷貝構造函數SafeStack s2 = s1; // 深拷貝// 修改s1不影響s2s1.pop();s1.push(99);// 測試賦值運算符SafeStack s3(2);s3 = s1; // 深拷貝賦值s1.print(); // 輸出: Stack: [ 1 2 99 ]s2.print(); // 輸出: Stack: [ 1 2 3 ]s3.print(); // 輸出: Stack: [ 1 2 99 ]// 測試移動語義SafeStack s4 = std::move(s1);s1.print(); // 輸出: Stack: [ ] (已移動)s4.print(); // 輸出: Stack: [ 1 2 99 ]return 0;
}