C++類和對象(下)

      網友投稿 822 2025-04-04

      @TOC


      零、前言

      本章為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; };

      雖然上述構造函數調用之后,對象中已經有了一個初始值,但是不能將其稱作為類對象成員的初始化構造函數體中的語句只能將其稱作為賦初值,而不能稱作初始化。因初始化只能初始化一次,而構造函數體內可以多次賦值(不符合初始化的基本原則)。

      示圖:

      總結:

      函數體賦值相當于是在對象成員變量創建之后再給成員變量賦值

      2、初始化列表

      概念:

      初始化較函數體賦值是更正規的初始化成員變量,當對象創建時,便是成員變量定義的時候,有些成員變量必須在定義的時候進行初始化,而這就是初始化列表的價值

      使用:

      以一個冒號開始,接著是一個以逗號分隔的數據成員列表,每個"成員變量"后面跟一個放在括號中的初始值或表達式

      示例:

      class Date { public: Date(int year, int month, int day) : _year(year)//定義時初始化 , _month(month) , _day(day) {} private: //這里相當于是聲明 int _year; int _month; int _day; };

      注意:

      每個成員變量在初始化列表中只能出現一次(初始化只能初始化一次)

      示圖:

      類中包含以下成員,必須放在初始化列表位置進行初始化:

      引用成員變量

      const成員變量

      自定義類型成員(該類沒有默認構造函數)

      注:我們都知道創建引用變量和const變量是必須初始化的,對于類來說,其引用變量和const變量成員必須在初始化列表進行初始化

      示例:

      class A { public: A(int a,int b)//是構造函數,但不是默認構造函數,不會在沒有參數的時候進行調用 :_a(a) ,_b(b) {} private: int _a; int _b; }; class B { public: B(int a, int ref) :_aobj(a,ref) , _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(); }

      輸出結果:

      解釋:

      成員變量在類中聲明次序就是其在初始化列表中的初始化順序,所以先初始化 _a2,而此時用 _a1還沒初始化為隨機值,再初始化 _a1

      總結:

      初始化列表是創建對象成員變量的時候給成員變量賦值,也就是初始化成員變量

      3、explicit關鍵字

      概念:

      構造函數不僅可以構造與初始化對象,對于單個參數的構造函數,還具有類型轉換的作用,而對于使用explicit修飾的構造函數,則禁止了這樣的轉換構造對象

      示例:

      class Date { public: Date(int year) :_year(year) {} private: int _year; int _month; int _day; }; void TestDate() { Date d1(2018); // 用一個整形變量給日期類型對象賦值 // 實際編譯器背后會用2019構造一個無名對象,最后用無名對象給d1對象進行賦值 d1 = 2019; }

      注:當構造函數加上explicit修飾后將無法進行這樣的類型轉化操作

      二、static成員

      概念:

      聲明為static的類成員稱為類的靜態成員,用static修飾的成員變量,稱之為靜態成員變量;用static修飾的成員函數,稱之為靜態成員函數。靜態的成員變量一定要在類外進行初始化(規定)

      相關面試題:

      實現一個類,計算中程序中創建出了多少個類對象

      實現代碼:

      class A { public: A() { ++_count; } A(const A& t) { ++_count; } static int GetACount() { return _count; } private: static int _count; }; int A::_count = 0; void TestA() { cout << A::GetACount() << endl; A a1, a2; A a3(a1); cout << A::GetACount() << endl; } int main() { TestA(); return 0; }

      C++類和對象(下)

      特性:

      靜態成員為所有類對象所共享,不屬于某個具體的實例

      靜態成員變量必須在類外定義,定義時不添加static關鍵字

      類靜態成員即可用類名::靜態成員或者對象.靜態成員來訪問

      靜態成員函數沒有隱藏的this指針,不能訪問任何非靜態成員

      靜態成員和類的普通成員一樣,也有public、protected、private3種訪問級別,也可以具有返回值

      相關問題:

      靜態成員函數可以調用非靜態成員函數嗎?

      不能,對于靜態成員函數并不是某個對象的具體實例,也就沒有this指針,無法調用非靜態成員函數

      非靜態成員函數可以調用類的靜態成員函數嗎?

      可以,靜態成員函數為所有類對象所共享

      示例:

      三、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; }

      注意:

      對于既有成員缺省值,又有默認構造函數(初始化列表和函數體賦值)時,對象創建時會先走初始化列表,再走函數體賦值,最后走成員缺省值

      四、友元

      友元分為:友元函數和友元類

      概念:

      友元提供了一種突破類域封裝的方式,有時提供了便利(非成員函數能夠訪問類私有成員)。但是友元會增加耦合度,破壞了封裝,所以友元不宜多用。

      1、友元函數

      有關問題引入:關于重載輸入輸出操作函數

      當重載輸出函數時,如果將函數設為類成員變量,我們發現cout\cin的輸出\輸入流對象和隱含的this指針在搶占第一個參數的位置。this指針默認是第一個參數也就是左操作數了。但是實際使用中cout\cin需要是第一個形參對象,才能正常使用。所以我們要operator<<重載成全局函數。但是這樣的話,又會導致類外沒辦法訪問成員,那么這里就需要友元來解決

      示例:重載成成員函數

      class Date { public: Date(int year, int month, int day) : _year(year) , _month(month) , _day(day) {} ostream& operator<<(ostream& _cout) { _cout <<_year << "-" <<_month << "-" <<_day; return _cout; } private: int _year; int _month; int _day; };

      使用友元函數:

      注:友元函數可以直接訪問類的私有成員,它是定義在類外部的普通函數,不屬于任何類,但需要在類的內部聲明,聲明時需要加friend關鍵字

      示例:

      class Date { friend ostream& operator<<(ostream& _cout, const Date& d); friend istream& operator>>(istream& _cin, Date& d); public: Date(int year=0, 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; }

      說明:

      友元可以訪問類私有和保護的成員,但不是類的成員函數

      友元函數不能用const修飾(不是類成員函數,沒有this指針)

      友元函數可以在類定義的任何地方聲明,不受類訪問限定符限制

      一個函數可以是多個類的友元函數

      友元函數的調用與普通函數的調用和原理相同

      2、友元類

      友元類的所有成員函數都可以是另一個類的友元函數,都可以訪問另一個類中的非公有成員。

      注意:

      友元關系是單向的,不具有交換性

      示例:Time類和Date類,在Time類中聲明Date類為其友元類,那么可以在Date類中直接訪問Time類的私有成員變量,但想在Time類中訪問Date類中私有的成員變量則不行

      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; };

      友元關系不能傳遞

      如果B是A的友元,C是B的友元,則不能說明C時A的友元

      五、內部類

      概念:

      如果一個類定義在另一個類的內部,這個內部類就叫做內部類,而另一個類為外部類

      注意:

      此時這個內部類是一個獨立的類,它不屬于外部類,更不能通過外部類的對象去調用內部類。外部類對內部類沒有任何優越的訪問權限

      內部類就是外部類的友元類

      注意:

      友元類的定義,內部類可以通過外部類的對象參數來訪問外部類中的所有成員,但是外部類不是內部類的友元

      特性:

      內部類可以定義在外部類的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;

      六、再次理解封裝

      C++是基于面向對象的程序,面向對象有三大特性即:封裝、繼承、多態

      C++通過類,將一個對象的屬性與行為結合在一起,使其更符合人們對于一件事物的認知,將屬于該對象的所有東西打包在一起;通過訪問限定符選擇性的將其部分功能開放出來與其他對象進行交互,而對于對象內部的一些實現細節,外部用戶不需要知道,知道了有些情況下也沒用,反而增加了使用或者維護的難度,讓整個事情復雜化

      封裝性的好處:

      示例:乘火車出行

      對于火車站系統:

      售票系統:負責售票----用戶憑票進入,對號入座

      工作人員:售票、咨詢、安檢、保全、衛生等

      火車:帶用戶到目的地

      火車站中所有工作人員配合起來,才能讓大家坐車有條不紊的進行,不需要知道火車的構造,票務系統是如何操作的,只要能正常方便的應用即可。

      想想下,如果是沒有任何管理的開放性站臺呢?火車站沒有圍墻,站內火車管理調度也是隨意,乘車也沒有規矩,比如:

      七、再次理解面向對象

      面向對象其實是在模擬抽象映射現實世界

      C 語言 C++

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:什么是 OKR(目標和關鍵結果)?了解如何使用 Google、Apple 等公司使用的管理工具
      下一篇:如何把固有的模式刪除掉
      相關文章
      亚洲国产精品一区第二页 | 久久99国产亚洲精品观看| 亚洲av一本岛在线播放| 亚洲国产日韩一区高清在线| 亚洲中文字幕无码久久2017| 国产成人精品日本亚洲专区61| 亚洲国产av无码精品| 亚洲高清国产拍精品熟女| 亚洲狠狠婷婷综合久久蜜芽| 亚洲精品美女久久久久久久| 亚洲爆乳大丰满无码专区| 亚洲暴爽av人人爽日日碰| www亚洲精品久久久乳| 亚洲AV成人无码网天堂| 亚洲av无码专区在线电影天堂| 亚洲高清一区二区三区电影| 老子影院午夜伦不卡亚洲| 国产亚洲精品美女| 亚洲国产一级在线观看 | 亚洲av高清在线观看一区二区| 亚洲av无码乱码在线观看野外| 亚洲成A∨人片天堂网无码| 亚洲成?v人片天堂网无码| 久久精品国产亚洲Aⅴ香蕉 | 亚洲人成影院午夜网站| 亚洲免费福利在线视频| 亚洲欧美国产日韩av野草社区| 国产亚洲福利精品一区二区| 亚洲精品无码av天堂| 亚洲精品乱码久久久久久蜜桃不卡| 亚洲AV午夜福利精品一区二区| 亚洲免费视频网站| 亚洲二区在线视频| 亚洲欧美国产国产一区二区三区| 亚洲AV电影天堂男人的天堂| 亚洲精品国产精品乱码不卞| 亚洲日本乱码在线观看| 久久久久亚洲Av片无码v| 亚洲人成网站18禁止久久影院| 亚洲高清有码中文字| 国产精品亚洲lv粉色|