1. 深入構造函數
1.1 函數體賦值
前文我們提到,創建對象時,編譯器會調用構造函數給成員變量賦值。但這并不能稱為對對象中成員變量的初始化。因為初始化只能初始化一次,但構造函數體內可以多次賦值。構造函數體中語句只能稱為賦初值
那么,成員變量初始化的地方在哪里?我們不得不引入一個知識:初始化列表
1.2 初始化列表
初始化列表:冒號開始,接著是逗號分隔的成員列表,每個“成員變量”后跟一個放括號中的初始值或表達式
class Date
{
private:int _year;int _month;int _day;public:Date(int year, int month, int day)//初始列表:_year(year),_month(month),_day(day)//函數體賦值{}
};
注意事項:
1. 每個成員變量在初始化列表最多出現一次(即只能初始化一次)
2. 類中包含:a.引用成員變量 b.const成員變量 c. 自定義成員函數(且該類沒有默認構造函數)
(a 和 b 是因為要求在定義時必須初始化;c 是因為會自動調用它的默認構造函數)
3. 盡量使用初始化列表初始化,對于自定義類型成員變量,一定會先使用初始化列表初始化
4. 成員變量在類中聲明次序是其在初始化列表中的初始化順序,和它在初始化列表的先后次序無關
class A
{
private:int _a;
public:A(int a): _a(a){}
};class B
{
private:A _ao; //沒有默認構造函數int& _ret; //引用const int _n; //const
public:B(int a, int ret):_ao(a),_ret(ret),_n(10){}
};
1.3 explicit關鍵字
對于接受單個參數的構造函數,還有類型轉換的作用。
接受單個參數的構造函數:
1. 構造函數只有一個參數
2. 構造函數有多個參數,除第一個參數沒默認值,其他參數都有默認值
3 .全缺省構造函數
但用explicit修飾構造函數,會禁止構造函數的隱式轉換
class Date
{
private:int _year;int _month;int _day;
public://單參數構造函數,有類型轉換的作用//用explicit修飾,禁止類型轉換explicit Date(int year):_year(year){}//有兩個缺省參數,相當于單個參數explicit Date(int year, int month=1, int day=1):_year(year),_month(month),_day(day){}
};
2. static成員
聲明為static的類成員為類的靜態成員,修飾成員變量稱為靜態成員變量;修飾成員函數稱為靜態成員函數。靜態成員變量必須在類外初始化
特性:
1. 靜態成員為所有類對象共享,存放在靜態區
2. 靜態成員變量必須在類外定義,類中只是聲明,定義不加static關鍵字
3. 靜態成員用 類名::靜態成員 或者 對象.靜態成員 訪問
4. 靜態成員函數沒有隱藏的this指針,不能訪問任何非靜態成員
5.靜態成員也是類的成員,受到訪問限定符的限制
3. 友元
友元可以突破類封裝的限制。但友元會增加耦合度,破壞封裝,不宜多用
友元:友元函數和友元類
3.1 友元函數
友元函數可以直接訪問類的私有成員,它是定義在類外部的普通函數,需要在類的內部聲明,聲明時要加friend關鍵字
注意事項:
1. 友元函數不是類的成員函數,只是可以訪問類的私有和保護成員
2. 友元函數不能用const修飾
3. 友元函數可以在類定義的任何位置聲明,不受訪問限定符的限制
4. 一個函數可以是多個函數的友元函數
5. 友元函數的調用和普通函數調用相同
適用場景:
//如果想重載operator<<,但無法重載為類成員函數。
//this指針默認是第一個參數也就是左操作數了。
//但是實際使用中cout需要是第一個形參對象,所以要將operator << 重載成全局函數。
// 但類外沒辦法訪問成員,此時就需要友元來解決。operator >> 同理
class Date
{friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year = 2025, int month = 1, int day = 1): _year(year), _month(month), _day(day){}private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{_cin >> d._year;_cin >> d._month;_cin >> d._day;return _cin;
}
3.2 友元類
友元類的所有成員函數都可以是另一個類的友元函數,可以訪問另一個類的成員
注意事項:
1. 友元關系單向,不具有交互性
2. 友元關系不能傳遞
3. 友元關系不能繼承
class Time
{//聲明日期類為時間類的友元類,在日期類中可以直接訪問Time類的私有成員變量friend class Date;
public:Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}private:int _hour;int _minute;int _second;
};class Date
{
public:Date(int year = 2025, int month = 1, int day = 1):_year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接訪問時間類私有的成員變量_t._hour = hour;_t._minute = minute;_t._second = second;}private:int _year;int _month;int _day;Time _t;
};
4. 內部類
一個類定義在另一個類的內部,這個內部的類就叫內部類。
它不屬于外部類,不能通過外部類的對象去訪問內部類的成員(外部類對內部類沒有任何超越的訪問權限),但內部類是外部類的友元類,內部類可以通過外部類對象參數訪問外部類中所有成員。
注意事項:
1. 內部類可以定義在外部類的任意地方(如同友元)
2. 內部類可以直接訪問外部類的static成員,不需要外部類的對象/類名
3. sizeof(外部類)= 外部類,相當于內部類不存儲在外部類
class A
{
private:static int a;int b;
public:// B是A的友元class B{public:void fun(const A& i){//不用外部類的對象cout << a << endl;cout << i.b << endl;}};
};
int A::a = 1;