🌈個人主頁:小新_-
🎈個人座右銘:“成功者不是從不失敗的人,而是從不放棄的人!”🎈
🎁歡迎各位→點贊👍 + 收藏?? + 留言📝
🏆所屬專欄:話說那些與C++的愛恨情仇? ?歡迎訂閱,持續更新中~~~
? ? ?? ?
? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ?讓小新帶著你快樂的學習吧~?
前言

1. 構造函數
1.1 概念
class Date
{
public:void Init(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;d1.Init(2022, 7, 5);d1.Print();Date d2;d2.Init(2022, 7, 6);d2.Print();return 0;
}
1.2 特性
?class Date{public:// 1.無參構造函數Date(){}// 2.帶參構造函數Date(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;};void TestDate(){Date d1; // 調用無參構造函數Date d2(2015, 1, 1); // 調用帶參的構造函數// 注意:如果通過無參構造函數創建對象時,對象后面不用跟括號,否則就成了函數聲明// 以下代碼的函數:聲明了d3函數,該函數無參,返回一個日期類型的對象// warning C4930: “Date d3(void)”: 未調用原型函數(是否是有意用變量定義的?)Date d3();}
?class Date{public:/*// 如果用戶顯式定義了構造函數,編譯器將不再生成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類中構造函數屏蔽后,代碼可以通過編譯,因為編譯器生成了一個無參的默認構造函
//數// 將Date類中構造函數放開,代碼編譯失敗,因為一旦顯式定義任何構造函數,編譯器將不再
//生成// 無參構造函數,放開后報錯:error C2512: “Date”: 沒有合適的默認構造函數可用Date d1;return 0;}
class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本類型(內置類型)int _year;int _month;int _day;// 自定義類型Time _t;
};
int main()
{Date d;return 0;
}
class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本類型(內置類型)int _year = 1970;int _month = 1;int _day = 1;// 自定義類型Time _t;
};
int main()
{Date d;return 0;
}
class Date
{
public:Date(){_year = 1900;_month = 1;_day = 1;}Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
// 以下測試函數能通過編譯嗎?
void Test()
{Date d1;
}
2.析構函數
2.1 概念
2.2 特性
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申請空間失敗!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;
}}
private:DataType* _array;int _capacity;int _size;
};
void TestStack()
{Stack s;s.Push(1);s.Push(2);
}
class Time
{
public:~Time(){cout << "~Time()" << endl;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本類型(內置類型)int _year = 1970;int _month = 1;int _day = 1;// 自定義類型Time _t;
};
int main()
{Date d;return 0;
}
程序運行結束后輸出:~Time()
?在main方法中根本沒有直接創建Time類的對象,為什么最后會調用Time類的析構函數?
?因為:main方法中創建了Date對象d,而d中包含4個成員變量,其中_year, _month,?
_day三個是?內置類型成員,銷毀時不需要資源清理,最后系統直接將其內存回收即可;而_t是Time類對象,所以在 d銷毀時,要將其內部包含的Time類的_t對象銷毀,所以要調用Time類的析構函數。但是:main函數中不能直接調用Time類的析構函數,實際要釋放的是Date類對象,所以編譯器會調用Date類的析構函 數,而Date沒有顯式提供,則編譯器會給Date類生成一個默認的析構函數,目的是在其內部調用Time類的析構函數,即當Date對象銷毀時,要保證其內部每個自定義對象都可以正確銷毀 main函數中并沒有直接調用Time類析構函數,而是顯式調用編譯器為Date類生成的默認析構函數
注意:創建哪個類的對象則調用該類的析構函數,銷毀那個類的對象則調用該類的析構函數
3.?拷貝構造函數
3.1 概念

3.2特征
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// Date(const Date& d) ? // 正確寫法Date(const Date& d) ? // 錯誤寫法:編譯報錯,會引發無窮遞歸{_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2(d1);return 0;
}
class Time
{
public:Time(){_hour = 1;_minute = 1;_second = 1;}Time(const Time& t){_hour = t._hour;_minute = t._minute;_second = t._second;cout << "Time::Time(const Time&)" << endl;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本類型(內置類型)int _year = 1970;int _month = 1;int _day = 1;// 自定義類型Time _t;
};
int main()
{Date d1;// 用已經存在的d1拷貝構造d2,此處會調用Date類的拷貝構造函數// 但Date類并沒有顯式定義拷貝構造函數,則編譯器會給Date類生成一個默認的拷貝構//造函數Date d2(d1);return 0;
}
// 這里會發現下面的程序會崩潰掉?這里就需要我們以后講的深拷貝去解決。
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申請空間失敗");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2(s1);return 0;
}


- 使用已存在對象創建新對象
- 函數參數類型為類類型對象
- 函數返回值類型為類類型對象
class Date
{
public:Date(int year, int minute, int day){cout << "Date(int,int,int):" << this << endl;}Date(const Date& d){cout << "Date(const Date& d):" << this << endl;}~Date(){cout << "~Date():" << this << endl;}
private:int _year;int _month;int _day;
};
Date Test(Date d)
{Date temp(d);return temp;
}
int main()
{Date d1(2022,1,13);Test(d1);return 0;
}

4.賦值運算符重載
4.1 運算符重載

注意:不能通過連接其他符號來創建新的操作符:比如 operator@重載操作符必須有一個類類型參數用于內置類型的運算符,其含義不能改變,例如:內置的整型 + ,不 能改變其含義作為類成員函數重載時,其形參看起來比操作數數目少 1 ,因為成員函數的第一個參數為隱藏的 this.* 、::、sizeof、 ? : 、. 注意以上 5 個運算符不能重載。這個經常在筆試選擇題中出現。

這里會發現運算符重載成全局的就需要成員變量是公有的,那么問題來了,假如把類的成員變量改為私有?則 operator 會報錯?,此時封裝性如何保證?
?這里其實可以用我們后面學習的友元解決,或者干脆重載成成員函數。
即當重載成為全局 無法訪問類的私有成員,解決方法:
1.提供get和set方法
2、友元(后面講)
3、重載為成員函數


4.2?賦值運算符重載

- 參數類型:const T&,傳遞引用可以提高傳參效率
- 返回值類型:T&,返回引用可以提高返回的效率,有返回值目的是為了支持連續賦值
- 檢測是否自己給自己賦值
- 返回*this :要復合連續賦值的含義
- 重載的賦值運算符函數參數只有一個,另一個通過this指針傳遞
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d)//拷貝構造{_year = d._year;_month = d._month;_day = d._day;}Date& operator=(const Date& d)//賦值拷貝{if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
};



4.3傳值返回和傳引用返回區別



class Time
{
public:Time(){_hour = 1;_minute = 1;_second = 1;}Time& operator=(const Time& t){if (this != &t){_hour = t._hour;_minute = t._minute;_second = t._second;}return *this;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本類型(內置類型)int _year = 1970;int _month = 1;int _day = 1;// 自定義類型Time _t;
};
int main()
{Date d1;Date d2;d1 = d2;return 0;
}
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申請空間失敗");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2;s2 = s1;return 0;
}
我們運行會中斷
4.4前置++和后置++的重載
class Date{public :Date ( int year = 1900 , int month = 1 , int day = 1 ){_year = year ;_month = month ;_day = day ;}// 前置 ++ :返回 +1 之后的結果// 注意: this 指向的對象函數結束后不會銷毀,故以引用方式返回提高效率Date & operator ++ (){_day += 1 ;return * this ;}// 后置 ++ :// 前置 ++ 和后置 ++ 都是一元運算符,為了讓前置 ++ 與后置 ++ 形成能正確重載// C++ 規定:后置 ++ 重載時多增加一個 int 類型的參數,但調用函數時該參數不用傳遞,編譯器自動傳遞// 注意:后置 ++ 是先使用后 +1 ,因此需要返回 +1 之前的舊值,故需在實現時需要先將 this 保存一份,然后給 this+1// ? ? ? 而 temp 是臨時對象,因此只能以值的方式返回,不能返回引用Date operator ++ ( int ){Date temp ( * this );_day += 1 ;return temp ;}private :int _year ;int _month ;int _day ;};int main (){Date d ;Date d1 ( 2022 , 1 , 13 );d = d1 ++ ; ? ? // d: 2022,1,13 ? d1:2022,1,14d = ++ d1 ; ? ? // d: 2022,1,15 ? d1:2022,1,15return 0 ;}
5.日期類的實現
為了檢驗這幾節的學習效果,如果可以手搓日期類就代表掌握了,讀者朋友可以參考并實現,以助于更好掌握該部分知識
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<assert.h>
class Date{public:// 獲取某年某月的天數int GetMonthDay(int year, int month){assert(month > 0 && month < 13);static int monthday[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };if (month == 2 && (year % 4 == 0 && year % 100 != 0) || year % 400 == 0){return 29;}else{return monthday[month];}}// 全缺省的構造函數Date(int year, int month, int day){_year = year;_month = month;_day = day;}// 拷貝構造函數// d2(d1)Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}// 賦值運算符重載// d2 = d3 -> d2.operator=(&d2, d3)Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}// 析構函數~Date(){_year = 0;_month = 0;_day = 0;}// 日期+=天數Date& operator+=(int day){if (day < 0){return*this -= -day;}_day += day;while (_day > GetMonthDay(_year, _month)){_day = _day - GetMonthDay(_year, _month);++_month;if (_month == 13){_month = 1;}}return*this;}// 日期+天數Date operator+(int day){Date tmp = *this;tmp += day;return tmp;}// 日期-天數Date operator-(int day){Date tmp = *this;tmp -= day;return tmp;}// 日期-=天數Date& operator-=(int day){if (day < 0){return *this += -day;}_day -= day;while (_day <= 0){--_month;if (_month == 0){_month = 12;_year--;}_day += GetMonthDay(_year, _month);}return *this;}// 前置++Date& operator++(){*this += 1;return*this;}// 后置++Date operator++(int){Date tmp(*this);*this += 1;return tmp;}// 后置--Date operator--(int){Date tmp(*this);*this -= 1;return tmp;}// 前置--Date& operator--(){*this -= 1;return*this;}// >運算符重載bool operator>(const Date& d){if (_year > d._year){return true;}else if (_year == d._year){if (_month > d._month){return true;}else if (_month == d._month){return _day > d._day;}}return false;}// ==運算符重載bool operator==(const Date& d){return _year == d._year && _month == d._month && _day == d._day;}// >=運算符重載bool operator >= (const Date& d){return *this > d || *this == d;}// <運算符重載bool operator < (const Date& d){return !(*this >= d);}// <=運算符重載bool operator <= (const Date& d){return !(*this > d);}// !=運算符重載bool operator != (const Date& d){return !(*this == d);}// 日期-日期 返回天數int operator-(const Date& d);void Print(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}private:int _year;int _month;int _day;};
int main()
{Date d1(2025, 12, 25);d1 -= 600;d1.Print();return 0;
}

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}void Print() const{cout << "Print()const" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
void Test()
{Date d1(2022,1,13);d1.Print();const Date d2(2022,1,13);d2.Print();
}

class Date
{
public:Date* operator&(){return this;}const Date* operator&()const{return this;}
private:int _year; // 年int _month; // 月int _day; // 日
};
最后,感謝大家的觀看!