C++從入門到精通(第四篇) :C++的基礎和靈魂:類和對象(下篇)
1. 再談構造函數
構造函數體賦值
在創建對象時,編譯器通過調用構造函數,給對象中各個成員變量一個合適的初始值
class Date { public: Date(int year, int month, int day) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; };
雖然上述構造函數調用之后,對象中已經有了一個初始值,但是不能將其稱作為類對象成員的初始化構造函數體中的語句只能將其稱作為賦初值,而不能稱作初始化。因初始化只能初始化一次,而構造函數體內可以多次賦值(不符合初始化的基本原則)。
初始化列表
初始化較函數體賦值是更正規的初始化成員變量,當對象創建時,便是成員變量定義的時候,有些成員變量必須在定義的時候進行初始化,而這就是初始化列表的價值
使用:
以一個冒號開始,接著是一個以逗號分隔的數據成員列表,每個"成員變量"后面跟一個放在括號中的初始值或表達式
class Date { public: Date(int year, int month, int day) : _year(year)//定義時初始化 , _month(month) , _day(day) {} private: //這里相當于是聲明 int _year; int _month; int _day; };
注意
每個成員變量在初始化列表中只能出現一次(初始化只能初始化一次)
類中包含以下成員,必須放在初始化列表位置進行初始化:
[ ] 引用成員變量
[ ] const成員變量
[ ] 自定義類型成員(該類沒有默認構造函數
class A { public: A(int a) :_a(a) {} private: int _a; }; class B { public: B(int a, int ref) :_aobj(a) ,_ref(ref) ,_n(10) {} private: A _aobj; // 沒有默認構造函數 int& _ref; // 引用 const int _n; // const };
盡量使用初始化列表初始化,因為不管你是否使用初始化列表,對于自定義類型成員變量,一定會先使
用初始化列表初始化
class Time { public: Time(int hour = 0) :_hour(hour) { cout << "Time()" << endl; } private: int _hour; }; class Date { public: Date(int day) {} private: int _day; Time _t; }; int main() { Date d(1); }
成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后次序無關
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(); }
結果:
explicit關鍵字
構造函數不僅可以構造與初始化對象,對于單個參數的構造函數,還具有類型轉換的作用,而對于使用explicit修飾的構造函數,則禁止了這樣的轉換構造對象
class Date { public: Date(int year) :_year(year) {} explicit Date(int year) :_year(year) {} private: int _year; int _month: int _day; }; void TestDate() { Date d1(2018); // 用一個整形變量給日期類型對象賦值 // 實際編譯器背后會用2019構造一個無名對象,最后用無名對象給d1對象進行賦值 d1 = 2019; }
上述代碼可讀性不是很好,用explicit修飾構造函數,將會禁止單參構造函數的隱式轉換
2.static成員
概念
聲明為static的類成員稱為類的靜態成員,用static修飾的成員變量,稱之為靜態成員變量;用static修飾的
成員函數,稱之為靜態成員函數。靜態的成員變量一定要在類外進行初始化
特性
靜態成員為所有類對象所共享,不屬于某個具體的實例
靜態成員變量必須在類外定義,定義時不添加static關鍵字
類靜態成員即可用類名::靜態成員或者對象.靜態成員來訪問
靜態成員函數沒有隱藏的this指針,不能訪問任何非靜態成員
靜態成員和類的普通成員一樣,也有public、protected、private3種訪問級別,也可以具有返回值
3.C++11 的成員初始化新玩法
C++11支持非靜態成員變量在聲明時進行初始化賦值,但是要注意這里不是初始化,這里是給聲明的成員變
量缺省值。
class B { public: B(int b = 0) :_b(b) {} int _b; }; class A { public: void Print() { cout << a << endl; cout << b._b << endl; cout << p << endl; cout << n << endl; } private: // 非靜態成員變量,可以在成員聲明時給缺省值 int a = 10; B b = 20; int* p = (int*)malloc(4); static int n; }; int A::n = 10; int main() { A a; a.Print(); return 0; }
4. 友元
友元分為:友元函數和友元類
友元提供了一種突破封裝的方式,有時提供了便利。但是友元會增加耦合度,破壞了封裝,所以友元不宜多
用。
友元函數
友元函數可以直接訪問類的私有成員,它是定義在類外部的普通函數,不屬于任何類,但需要在類的內部聲
明,聲明時需要加friend關鍵字。
例:
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) {} private: int _year; int _month; int _day; }; ostream& operator<<(ostream& _cout, const Date& d) { _cout< 說明: 友元函數可訪問類的私有和保護成員,但不是類的成員函數 友元函數不能用const修飾 友元函數可以在類定義的任何地方聲明,不受類訪問限定符限制 一個函數可以是多個類的友元函數 友元函數的調用與普通函數的調用和原理相同 友元類 友元類的所有成員函數都可以是另一個類的友元函數,都可以訪問另一個類中的非公有成員。 友元關系是單向的,不具有交換性。 比如上述Time類和Date類,在Time類中聲明Date類為其友元類,那么可以在Date類中直接訪問Time 類的私有成員變量,但想在Time類中訪問Date類中私有的成員變量則不行。 友元關系不能傳遞 如果B是A的友元,C是B的友元,則不能說明C時A的友元。 class Date; // 前置聲明 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; }; 5.內部類 概念及特性 概念: 如果一個類定義在另一個類的內部,這個內部類就叫做內部類。 注意此時這個內部類是一個獨立的類,它不屬于外部類,更不能通過外部類的對象去調用內部類。外部類對內部類沒有任何優越的訪問權限。 注意:內部類就是外部類的友元類。注意友元類的定義,內部類可以通過外部類的對象參數來訪問外部類中 的所有成員。但是外部類不是內部類的友元。 特性: 內部類可以定義在外部類的public、protected、private都是可以的。 注意內部類可以直接訪問外部類中的static、枚舉成員,不需要外部類的對象/類名。 sizeof(外部類)=外部類,和內部類沒有任何關系 例: class A { private: static int k; int h; public: class B { public: void foo(const A& a) { cout << k << endl;//無需對象訪問靜態成員 cout << a.h << endl;//通過對象參數訪問成員變量 } }; }; int A::k = 1; 6. 再次理解封裝 C++是基于面向對象的程序,面向對象有三大特性即:封裝、繼承、多態。 C++通過類,將一個對象的屬性與行為結合在一起,使其更符合人們對于一件事物的認知,將屬于該對象的 所有東西打包在一起;通過訪問限定符選擇性的將其部分功能開放出來與其他對象進行交互,而對于對象內 部的一些實現細節,外部用戶不需要知道,知道了有些情況下也沒用,反而增加了使用或者維護的難度,讓 整個事情復雜化。 封裝性的好處: 示例:乘火車出行 我們來看下火車站: 售票系統:負責售票----用戶憑票進入,對號入座 工作人員:售票、咨詢、安檢、保全、衛生等 火車:帶用戶到目的地 火車站中所有工作人員配合起來,才能讓大家坐車有條不紊的進行,不需要知道火車的構造,票務系統是如 何操作的,只要能正常方便的應用即可。 想想下,如果是沒有任何管理的開放性站臺呢?火車站沒有圍墻,站內火車管理調度也是隨意,乘車也沒有 規矩,比如: 7.再次理解面向對象 可以看出面向對象其實是在模擬抽象映射現實世界。 感覺博主講得不錯的話,點個關注吧! C 語言 C++
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。