目錄
const成員函數:
前置++和后置++重載:
取地址及const取地址操作符重載:
初始化列表:
explicit關鍵字:
static成員:
友元:
友元函數:
友元類:
內部類:
匿名對象:
拷貝對象時的一些編譯器優化:
const成員函數:
const修飾的成員函數是const成員函數,修飾成員函數,實際上修飾的是成員函數隱藏的this指針,表明在該函數中不能對類的任何成員進行修改。
1. const對象可以調用非const成員函數嗎?不可以
2. 非const對象可以調用const成員函數嗎?可以
3. const成員函數內可以調用其它的非const成員函數嗎?不可以
4. 非const成員函數內可以調用其它的const成員函數嗎?可以
總結來說:權限只可縮小和平移,不可以放大。const調用非const就是一種權限放大。
前置++和后置++重載:
C++規定:后置++重載時多增加一個int 類型的參數,但是調用函數時該參數不用傳遞,編譯器自動傳遞。
//前置++:
Data& Data::operator++()
{*this += 1;// += 這里是在類中重載過的新 += 。return *this;
}
//后置++:注意后置++是先使用再加1,因此需要返回+1之前的舊值
Data Data::operator++(int)
{Data d(*this);*this += 1;return d;
}
取地址及const取地址操作符重載:
這兩個默認成員函數一般不需要管,編譯器自動生成,只有特殊情況,才需要自己寫,比如:讓別人獲取指定的內容。
class Dat
{
public :Date* operator&(){return this ;}const Date* operator&()const{return this ;}
private :int _year ; // 年int _month ; // 月int _day ; // 日
}
初始化列表:
構造函數體內給對象賦值,但是還不能叫做初始化,只有叫賦初值,初始化只能初始化一次,而構造函數體內可以多次賦值,所以就引出了初始化列表。
初始化列表以冒號開始,逗號分隔,括號里是初始值或者表達式。每個成員變量定義初始化的地方,初始化列表定義的時候需要值,缺省值就是給它的。
class Data
{
public:Data(int year = 2025, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};
1.每個成員變量在初始化列表中只能出現一次(初始化只能初始化一次)。
2.類中包含以下成員,必須放在初始化列表位置進行初始化:1.引用成員變量 2.const成員變量 3.自定義類型成員(且該類沒有默認構造函數)
3.盡量使用初始化列表初始化,因為不管你是否使用初始化列表,對于自定義類型成員變量, 一定會先使用初始化列表初始化。
4.成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后 次序無關
總結:能用初始化列表就用。
explicit關鍵字:
構造函數不僅可以構造與初始化對象,對于單個參數或者除第一個參數無默認值其余均有默認值 的構造函數,還具有類型轉換的作用。
單參數的構造函數支持隱式類型轉換(隱式類型轉換會產生臨時變量)。
上述可以編譯成功 ,實際就是隱式類型轉換,轉換時編譯器產生一個臨時變量,用臨時變量對d1進行賦值,臨時變量是一個無名對象。
用explicit修飾后就失敗了。禁止了隱式類型轉換。要支持隱式類型轉換需要賦值運算符重載,因為我沒寫,所以編譯器自動生成了一個。
static成員:
聲明為static的類成員稱為類的靜態成員,用static修飾的成員變量,稱之為靜態成員變量;用 static修飾的成員函數,稱之為靜態成員函數。靜態成員變量一定要在類外進行初始化
特性:
1. 靜態成員為所有類對象所共享,不屬于某個具體的對象,存放在靜態區
2. 靜態成員變量必須在類外定義,定義時不添加static關鍵字,類中只是聲明
3. 類靜態成員即可用 類名::靜態成員 或者 對象.靜態成員 來訪問
4. 靜態成員函數沒有隱藏的this指針,不能訪問任何非靜態成員
5. 靜態成員也是類的成員,受public、protected、private 訪問限定符的限制
實現一個類,計算程序中創建了多少個類對象:
?
class A
{
public:A(){++_scount; }A(const A& t) {++_scount; }~A() { --_scount; }static int GetACount() {return _scount; }
private:static int _scount;
};int A::_scount = 0;void TestA()
{cout << A::GetACount() << endl;A a1, a2;A a3(a1);cout << A::GetACount() << endl;
}int main()
{TestA();
}
這個例子很好的說明了,靜態類成員是所有對象共享的,靜態成員函數只可訪問靜態成員變量(因為沒有this指針)。
非靜態成員函數可以調用靜態成員函數,但是反之不行,因為非靜態成員必須是對象定義之后才有實際空間,才可以使用成員函數,不然都只是模型而已。
友元:
友元提供了一種突破封裝的方式,有時提供了便利。但是友元會增加耦合度,破壞了封裝,所以 友元不宜多用。 友元分為:友元函數和友元類
友元函數:
友元函數可以直接訪問類的私有成員,它是定義在類外部的普通函數,不屬于任何類,但需要在 類的內部聲明,聲明時需要加friend關鍵字。
下面看個實例:重載 operator<< 和 >> ,因為重載成成員函數,cout的輸出流對象和隱含的this指針在搶占第一個參數的位置,所以重載函數必須成全局函數,但是又會導致類外無法訪問成員,就需要友元:
class Date
{friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year = 1900, 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;
}int main()
{Date d;cin >> d;cout << d << endl;
}
友元函數可以直接訪問類的私有成員,它是定義在類外部的普通函數,不屬于任何類,但需要在 類的內部聲明,聲明時需要加friend關鍵字。
注意:
1.友元函數可訪問類的私有和保護成員,但不是類的成員函數。
2.友元函數不能用const修飾 。
3.友元函數可以在類定義的任何地方聲明,不受類訪問限定符限制。
4.一個函數可以是多個類的友元函數。
5.友元函數的調用與普通函數的調用原理相同
友元類:
友元類的所有成員函數都可以是另一個類的友元函數,都可以訪問另一個類中的非公有成員。
友元關系是單向的,不具有交換性。
比如下述Time類和Date類,在Time類中聲明Date類為其友元類,那么可以在Date類中直接訪問Time類的私有成員變量,但想在Time類中訪問Date類中私有的成員變量則不行。
友元關系不能傳遞 ,如果C是B的友元,B是A的友元,則不能說明C時A的友元。
class Time
{friend class Date;// 聲明日期類為時間類的友元類,則在日期類中就直接訪問Time類中的私有成員變量
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 = 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;
};
內部類:
概念:如果一個類定義在另一個類的內部,這個內部類就叫做內部類。內部類是一個獨立的類, 它不屬于外部類,更不能通過外部類的對象去訪問內部類的成員。外部類對內部類沒有任何優越 的訪問權限。內部類也受訪問限制符的限制。
注意:內部類就是外部類的友元類,參見友元類的定義,內部類可以通過外部類的對象參數來訪 問外部類中的所有成員。但是外部類不是內部類的友元。(內可以訪問外部,外部不能訪問內部)
特性: 1. 內部類可以定義在外部類的public、protected、private都是可以的。
2. 注意內部類可以直接訪問外部類中的static成員,不需要外部類的對象/類名。
3. sizeof(外部類)=外部類,和內部類沒有任何關系。
匿名對象:
匿名對象的生命周期只有一行。
class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}
private:int _a;
};int main()
{A aa1;// 定義普通對象不能 A aa1();因為編譯器分不清是函數聲明,還是對象定義//定義匿名對象,匿名對象的特點就是不用取名字,生命周期只有這一行,接下來就會調用析構函數。A();
}
拷貝對象時的一些編譯器優化:
在傳參和傳返回值的過程中,一般編譯器會做一些優化,減少對象的拷貝,提高效率。這個在一些場景下還 是非常有用的。
1.隱式類型,連續構造+拷貝構造->優化為直接構造
2.一個表達式中,連續構造+拷貝構造->優化為一個構造
3.一個表達式中,連續拷貝構造+拷貝構造->優化一個拷貝構造
4.一個表達式中,連續拷貝構造+賦值重載->無法優化