引入
之前,我們談論了關於類的基本概念,瞭解了類和對象的基本使用以及關於類的六個默認構造函數等。那麼今天就來說一說在C++類中你可能還不太瞭解的黑暗料理~~ 學會了之後,你會發現,原來還可以這麼玩。。
再談構造函數之初始化列表
class Date{
public:
Date(int year,int month,int day){
_year = year;
_month = month;
_day = day;
}
private:
int _year; //聲明
int _month;
int _day;
}
上面我寫了一個簡單的日期類函數,雖然調用構造函數之後,對象中已經有了一個初值,但不能稱爲類對象成員的初始化,構造函數體內的語句只能稱之爲賦初值,而不是初始化。因爲初始化只能初始化一次,但是構造函數體內可以被多次賦值。 那麼類對象成員到底是怎樣進行初始化的呢?
初始化列表
在C++中,引入了初始化列表這個概念,專門用來進行對類對象成員函數初始化,以一個冒號開始,接着是一個以逗號分隔的數據成員列表,每個"成員變量"後面跟一個放在括號中的初始值或表達式。
class Date{
public:
Date(int year,int month,int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year; //聲明
int _month;
int _day;
}
注:
- 每個成員變量只能在初始化列表中出現一次,(初始化只能被初始化一次)。
- 類中包含const成員變量,引用成員變量,類類型成員變量(該類沒有默認的構造函數)必須在初始化列表中初始化。這裏的問題就是,這三類成員必須在定義的同時進行初始化。
- 成員變量在類中的聲明次序就是成員變量在初始化列表中的初始化順序,與列表中的先後順序無關。
explicit關鍵字
對於explicit關鍵字,我相信大多初學者跟我一樣,也不知道它是用來做什麼的,那是我們自己把構造函數想的過於簡單了,其實構造函數不僅僅可以用來初始化,對於單個參數或者n個參數,但是n-1個參數提供了默認值這樣的構造函數還具有類型轉換的功能。
class A{
public:
A(){};
A(int a);
A(int a,int b);
A(int a,int b = 10);
~A();
private:
int _a;
int _b;
}
無參構造函數因爲沒有參數,故不能進行類型轉換,單個參數的構造函數可以進行類型轉換,例如 A aa; aa = 12.2; 這裏就會將12.2 轉換爲A類型。同理第三個構造函數不行,第四個可以。我們再來看看加上explicit關鍵字。
class A{
public:
A(){};
explicit A(int a);
A(int a,int b);
explicit A(int a,int b = 10);
~A();
private:
int _a;
int _b;
}
加上explicit關鍵字之後,就不允許單參構造函數進行隱式類型轉換。
static成員
- 概念:聲明爲static成員變量稱爲靜態成員變量,聲明爲static成員函數稱爲靜態成員函數。靜態成員變量要在類外初始化。
- 特性:
靜態成員爲所有對象共享,不屬於某個實例
靜態成員變量必須在類外進行初始化,不能加static關鍵字。
靜態成員可以用類::靜態成員 或者 對象.靜態成員訪問。
靜態成員沒有this指針,所以它不能訪問非靜態成員。
靜態成員函數與普通成員函數一樣,也具有三種訪問權限。
不能將靜態成員函數定義爲虛函數。
class A{
public:
A();
void print();
static int show();
private:
int _a;
static int _b;
}
//對靜態成員變量要在類外進行初始化
int A::a = 10;
void Test(){
A::show(); //訪問形式
A aa;
aa._b = 20;
}
static關鍵字的用法總結
static關鍵字在C/C++中都可以用來修飾內置類型變量以及函數。
在C語言中
- static修飾局部變量:static修飾的局部變量將會改變它的生命週期,將其從當前代碼塊改變爲整個程序。
- static修飾全局變量以及全局函數:static修飾全局變量以及全局函數將會改變它的鏈接屬性,改變了它的作用域,將其改變爲當前文件,這就起到了一個隱藏作用,因爲未加static關鍵字的全局變量和函數都具有全局可見性,其它源程序也能訪問到,有可能就產生了命名衝突等問題。 應用全局變量需要用extern 進行聲明。
- static默認初始化爲0,其實全局變量也具備這一屬性,因爲全局變量也存儲在靜態存儲區,在靜態存儲區當中,內存中所有字節默認值都是0x00。
在C++中
- static關鍵字通常用來聲明類成員函數以及成員變量。聲明爲靜態成員之後,該成員爲整個類所共享,不屬於某個對象。使用細節如上面static的特性一樣。
友元
友元函數
- 概念:友元分爲友元類以及友元函數,友元提供了一種突破封裝的方式,提供了便利,但同時它也會增加耦合度,破壞封裝,所以友元不易多用。
- 爲什麼要使用友元?
如果我們嘗試去重載operator<<,然後發現我們沒辦法將operator<<重載成成員函數。因爲cout的輸出流對象和隱含的this指針在搶佔第一個參數的位置。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,int month,int day)
: _year(year)
, _month(month)
, _day(day)
{}
//重載輸出
ostream& operator<<(ostream& _cout,const Date& d){
_cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
return _cout;
}
//重載輸入
istream& operator>>(istream& _cin,Date& d){
_cin>>_year;
_cin>>_month;
_cin>>_day;
return _cin;
}
private:
int _year; //聲明
int _month;
int _day;
}
注:
- 友元函數可訪問類的私有成員,但不是類的成員函數
- 友元函數不能用const修飾
- 友元函數可以在類定義的任何地方聲明,不受類訪問限定符限制
- 一個函數可以是多個類的友元函數
- 友元函數的調用與普通函數的調用和原理相同
友元類
- 友元類的所有成員函數都可以是另一個類的友元函數,都可以訪問另一個類中的非公有成員。
- 友元關係是單向的,不具有交換性。如果B是A的友元,C是B的友元,則不能說明C時A的友元。
class Date; // 前置聲明
class Time
{
friend class Date; // 聲明日期類爲時間類的友元類
public:
Time(int hour, int minute, int second)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, 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;
};
內部類
- 內部類就是定義在一個類的內部,這個類是一個獨立的類,它不屬於外部類。注:內部類是外部類的友元,但外部類不是內部類的友元。
- 內部類可以定義在外部類的public、protected、private都是可以的。
- 注意內部類可以直接訪問外部類中的static、枚舉成員,不需要外部類的對象/類名。
- sizeof(外部類)=外部類,和內部類沒有任何關係。