C++類和對象補充
一. 再看構(gòu)造函數(shù)
我們之前已經(jīng)了解了構(gòu)造函數(shù)的基本內(nèi)容,那么這里我們將深入認識構(gòu)造函數(shù)。
1.函數(shù)體內(nèi)賦初值
class Date { public: Date(int year, int month, int day) { _year = year; _month = month; _day = day; //可以進行多次賦值,但一般不這么做 _year = 1; } private: int _year; int _month; int _day; };
首先,對于構(gòu)造函數(shù)體內(nèi)的賦值我們不能稱之為初始化。首先我們要理解:初始化只能初始化一次,而構(gòu)造函數(shù)體內(nèi)可以多次賦值。那么對象成員變量的初始化是在什么時候進行的呢?這就要接下來要介紹的初始化列表要做的事了。
2.初始化列表
初始化列表是以一個冒號開始,接著是一個以逗號分隔的數(shù)據(jù)成員列表,每個"成員變量"后面跟一個放在括號中的初始值或表達式。其形式如下:
class Date { public: Date(int year = 0, int month = 1, int day = 1) :_year(year) ,_month(month) { _day = day; } private: int _year; int _month; int _day; };
幾點注意
1.每個成員變量在初始化列表中只能出現(xiàn)一次(初始化只能初始化一次)
2.類中包含以下成員,必須放在初始化列表位置進行初始化:
(1)const成員變量:由于const變量初始化之后就不能更改,因此需在初始化列表進行初始化。
(2)引用成員變量:引用成員變量只能作為一個變量的引用,一旦初始化,就不能再作為其他變量的引用,因此引用變量也只能再初始化列表初始化。
(3)自定義類型成員變量(沒有默認構(gòu)造函數(shù)情況下):由于沒有默認構(gòu)造函數(shù)時,自定義類型變量是不能初始化的,此時程序也無法編譯,因此沒有默認構(gòu)造函數(shù)的自定義類型成員變量必須在初始化列表進行初始化。
class B { public: B(int i) :_i(i) { } private: int _i; }; class A { public: A(int a, int& b, int bb) :_a(a) ,_b(b) ,_bb(bb) { } private: const int _a;//const成員變量 int& _b;//引用成員變量 B _bb;//自定義成員變量 };
3.盡量使用初始化列表初始化,因為不管你是否使用初始化列表,對于自定義類型成員變量,一定會先使用初始化列表初始化。比如下面代碼的執(zhí)行結(jié)果:
class B { public: B() { cout << "B()" << endl; } private: int _i; }; class A { public: A(int a, int& b) :_a(a) ,_b(b) { } private: const int _a;//const成員變量 int& _b;//引用成員變量 B _bb;//自定義成員變量 }; int main() { int n = 0; A a1(0, n); return 0; }
可以看到,初始化列表中并沒有對自定義變量_bb初始化,但程序仍然調(diào)用了自定義類型的默認構(gòu)造函數(shù)。
4. 成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后次序無關,先想想下面的代碼運行結(jié)果是什么:
class A { public: A(int a) :_a1(a) , _a2(_a1) {} void Print() { cout << _a1 << " " << _a2 << endl; } private: int _a2; int _a1; }; int main() { A aa(1); aa.Print(); return 0; }
可以看到的是,_a1為1,而_a2為隨機值,這是因為在成員列表的聲明中,_a2先被聲明,_a1后被聲明,因此初始化列表中的順序是先_a2,后_a1。而一開始_a1為隨機值,因此最終_a2為隨機值。
3.explicit關鍵字
我們知道,對于構(gòu)造函數(shù),不僅可以構(gòu)造和初始化對象,對于單個參數(shù)的構(gòu)造函數(shù),還具有類型轉(zhuǎn)換的作用。
比如Date類:
class Date { public: Date(int year) :_year(year) {} explicit Date(int year) :_year(year) {} private: int _year; int _month; int _day; }; int main() { Date d1(2020); // 用一個整形變量給日期類型對象賦值 // 實際編譯器背后會用2019構(gòu)造一個無名對象,最后用無名對象給d1對象進行賦值 Date d2 = 2021;//explict禁止隱式類型轉(zhuǎn)換,因此該句代碼運行錯誤 }
但是Date d2 = 2021
;這樣的代碼可讀性不是很好,因此可以使用explicit關鍵字將這種隱式類型轉(zhuǎn)換禁止。
二.static成員
C語言中我們就接觸了static關鍵字,那么這個關鍵字修飾成員會怎么樣呢?
1.概念
聲明為static的類成員稱為類的靜態(tài)成員,用static修飾的成員變量,稱之為靜態(tài)成員變量;用static修飾的成員函數(shù),稱之為靜態(tài)成員函數(shù)。像上面初始化列表中說的,靜態(tài)的成員變量一定要在類外進行初始化。
2.特性
靜態(tài)成員存儲在靜態(tài)區(qū),為所有類對象所共享,不屬于某個具體的實例
靜態(tài)成員變量必須在類外定義,定義時不添加static關鍵字
類靜態(tài)成員即可用類名::靜態(tài)成員或者對象.靜態(tài)成員來訪問
靜態(tài)成員函數(shù)沒有隱藏的this指針,不能訪問任何非靜態(tài)成員;相對的,非靜態(tài)成員函數(shù)可以通過this指針訪問靜態(tài)成員變量。
靜態(tài)成員和類的普通成員一樣,也有public、protected、private 3種訪問級別,也可以具有返回值
接下來我們來看看一道題:
求1+2+3+…+n
題目描述:求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。
這道題我們可以利用構(gòu)造函數(shù),由于每次實例化對象,都會調(diào)用其構(gòu)造函數(shù),因此我們可以實例化n個對象,每次初始化時計算求和即可;
class Sum { public: //調(diào)用構(gòu)造函數(shù) Sum() { _sum += _i; ++_i; } //static修飾的成員函數(shù),沒有隱含的this指針,只能訪問靜態(tài)成員變量 static int GetSum() { return _sum; } private: //static修飾的成員變量為所有定義出來的類對象共有 static int _i; static int _sum; }; //靜態(tài)成員變量的定義 int Sum::_i = 1; int Sum::_sum = 0; class Solution { public: int Sum_Solution(int n) { Sum* p = new Sum[n]; return Sum::GetSum(); } };
【注意】sizeof(類名)不計算靜態(tài)成員變量的大小。比如上述代碼中的sizeof(Sum)為1,是一個空類。
三.友元
友元分為友元函數(shù)和友元類,其提供了一種突破封裝的方式,有時提供了便利。但是友元會增加耦合度,破壞了封裝,所以友元不宜多用。
1.友元函數(shù)
首先如果我們要重載<<(流插入)運算符,我們會發(fā)現(xiàn)將其定義成類成員函數(shù)將無法實現(xiàn),這是因為類成員函數(shù)的第一個參數(shù)為this指針,那么我們只能將這個函數(shù)定義在類外,但是這樣的話函數(shù)又不能訪問類中的成員變量,那么這個時候要么在成員函數(shù)中實現(xiàn)訪問的方法,要么就使用友元函數(shù),使其可以訪問類中成員。即:
class Date { //用關鍵字friend在類中聲明函數(shù)為Date的友元函數(shù) friend ostream& operator<<(ostream& out, const Date& d); public: Date(int year, int month, int day) :_year(year) ,_month(month) ,_day(day) {} private: int _year; int _month; int _day; }; ostream& operator<<(ostream& out, const Date& d) { out << d._year << "/" << d._month << "/" << d._day; return out; } int main() { Date d1(2021, 10, 20); cout << d1 << endl; }
同理,cin也可以如此定義。
【說明】
1.友元函數(shù)可訪問類的私有和保護成員,但不是類的成員函數(shù)
2.友元函數(shù)不能用const修飾
3.友元函數(shù)可以在類定義的任何地方聲明,不受類訪問限定符限制
4.一個函數(shù)可以是多個類的友元函數(shù)
5.友元函數(shù)的調(diào)用與普通函數(shù)的調(diào)用和原理相同
2.友元類
和友元函數(shù)相似,友元類可以訪問另一個類的私有成員。比如下面代碼中,B作為A的友元類,可以訪問A中的_a和_i。
class A { //聲明B為A的友元類,則在B中可以訪問A中的成員 friend class B; public: A(int a) :_a(a) { } private: int _a; static int _i; }; class B { public: B(int b) :_b(b) {} static int Count() { A::_i++; return A::_i; } private: int _b; }; int A::_i = 0; int main() { A a1(1); B b1(1); cout << b1.Count() << endl; cout << b1.Count() << endl; return 0; }
需要注意,友元關系是單向的,不具有交換性,比如上述代碼中A不能訪問B中的成員;友元關系不能傳遞,即B是A的友元,C是B的友元,但C不是A的友元,C就不能訪問A中的私有成員。
四.內(nèi)部類
顧名思義,定義在另一個類中的類就是內(nèi)部類。注意此時這個內(nèi)部類是一個獨立的類,它不屬于外部類,更不能通過外部類的對象去調(diào)用內(nèi)部類。外部類對內(nèi)部類沒有任何優(yōu)越的訪問權(quán)限。
內(nèi)部類就是外部類的友元類。注意友元類的定義,內(nèi)部類可以通過外部類的對象參數(shù)來訪問外部類中的所有成員。但是外部類不是內(nèi)部類的友元。
class A { public: class B//內(nèi)部類,是A的友元類 { public: //B可以直接訪問A的成員 void Print(const A& a) { cout << a._a << endl; cout << _i << endl; } }; A(int a) :_a(a) {} private: int _a; static int _i; }; int main() { A::B b1;//注意B的調(diào)用方式 A a1(1); b1.Print(a1); //但A的對象不能去訪問B中的成員 a1.b1;//error }
特性:
1.內(nèi)部類可以定義在外部類的public、protected、private都是可以的。
2.注意內(nèi)部類可以直接訪問外部類中的static、枚舉成員,不需要外部類的對象/類名。
3.sizeof(外部類)=外部類,和內(nèi)部類沒有任何關系。比如上面的sizeof(A)為4。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內(nèi)容!
相關文章
C++ OpenCV實現(xiàn)灰度圖蒙版GrayMask的示例代碼
這篇文章主要為大家詳細介紹了如何利用C++和OpenCV實現(xiàn)灰度圖蒙版GrayMask,文中的示例代碼講解詳細,對我們學習或工作有一定參考價值,需要的可以參考一下2022-05-05C++的get()函數(shù)與getline()函數(shù)使用詳解
這篇文章主要介紹了C++的get()函數(shù)與getline()函數(shù)使用詳解,是C++入門學習中的基礎知識,需要的朋友可以參考下2015-09-09