C++類和對象專題

內容總結

c++數據類型分爲簡單數據類型和複雜數據類型,前者也就是c++內置的基本數據類型,如int 、double等,後者又分爲c++內置的複合類型和自定義的類型。所以,歸根結底,我們現在自定義的類本質上來說就是作爲c++的一種特殊的數據類型,我們將各種複雜的數據和操作封裝到一個類裏面,所以在形式上,類的使用和定義本質上與基本類型類似,但是正是由於其拓展了數據類型的形式,所以出現了各種各樣的問題,從而引申出了友元函數、重載等各種各樣的知識點,另外也出現了深複製淺複製等問題。在這裏插入圖片描述

結構體

首先,我們接觸到了結構體,簡單來說,結構體就是由多種成員構成的一種複合類型。把不同類型、不同含義的數據當作一個整體來處理,結構體也就是具有相同類型或不同類型的數據構成的數據集合,也叫結構。
使用結構體,必須要先聲明一個結構體類型,再定義和使用結構體變量。

struct 類型名{
    數據類型1   成員名1;
    數據類型2   成員名2;} 變量名;

需要注意的是,結構體定義後需要加分號!!
在 C++ 中,允許在結構體中定義成員函數,也就是說C++的結構體成員包括數據成員和成員函數兩部分。
成員函數還可以根據需要定義重載運算符。

類型名 operator 運算符 (const 類型名 變量)const{}

這裏不要忘記const限定!
結構體的使用和普通類型類似,另外還可以在定義之後直接聲明。(如上)
結構體和普通數據類型一樣,也可以形成結構體數組。
結構體除了整體賦值操作外一般不對整體操作,一般對結構體變量的成員進行操作結構體變量名. 成員名

類是對具有相同屬性和行爲的一類客觀事物的概括描述。是用戶自定義的數據類型(程序設計語言角度)類的定義包括行爲和屬性兩個部分。屬性以數據表示,行爲通過函數實現。
類的定義

class  類名
{
   public: //類的外部接口
           公有數據成員和成員函數;
   protected: //僅允許本類成員函數及派生類成員函數訪問
           保護數據成員和成員函數;
   private: //僅允許本類成員函數訪問
           私有數據成員和成員函數;
}// 各成員函數的實現;
   返回值類型  類名::成員函數名(參數表)
{
          函數體
} 

和結構體一樣,一定不要忘記最後的分號!
其實歸根結底類和結構體非常相近,唯一區別就是在沒有明確指定類成員的訪問權限時,C++結構體的成員是公有的,而類的成員是私有的。
對於類的定義,爲了維護類的封裝性,一般定義數據成員爲私有的,而成員函數一般定義爲共有的。因此,如果想要通過類外訪問某一個類的數據成員,需要在成員函數定義get、set函數,通過這些共有的方法來訪問私有成員。當然,在軟件開發過程中,需要考慮權限的問題,也就是哪些類可以get、set,哪些類不行,從而保證數據的安全性。
這裏還要注意 ,在類的定義中不能對數據成員進行初始化,真要初始化需要用初始化列表。
關於類的使用還要注意,一般不用定義全局變量,否則會破壞封裝性,所以類的成員函數只能操作類的成員和類的局部變量。

->成員函數能操作的數據:

  • 類內的數據成員
  • 函數內定義的局部變量
  • 全局變量(一般不要定義全局變量)

類的成員函數通過操作數據成員來實現功能的。
若操作時需要從外界獲取數據的時候需要加形參。
若只對數據成員操作,則沒必要加形參

類的成員可以是其他類的對象,稱爲類的組合。但不能以類自身的對象作爲本類的成員。但是可以是本身的指針或者引用。
具體應用:
不使用結構體實現單鏈表
另外,關於類的對象空間管理,每個對象的數據成員都有單獨獨立的空間,而所有的函數全部存在一個共有空間裏。
在這裏插入圖片描述

對象

對象是類的實例或實體。根據上面提到的類的本質,類是一種自定義的數據類型,歸根結底還是一種數據類型,所以,類和對象的關係其實就相當於C++基本數據類型和該類型的變量之間的關係。 對象的定義也和基本數據類型保持形式一致。
和結構體一樣,必須在定義了類之後,纔可以定義類的對象,這其實也好理解,就相當於沒有爸爸哪來的兒子呢?
對象是類的實例化,要想使用類的功能,必須定義對象,所以對對象成員的訪問包括:
●圓點訪問形式:對象名.公有成員
●指針訪問形式:對象指針變量名->公有成員

對象的創建一定要按照構造函數的格式創建!

構造函數

構造函數是用於創建對象的特殊的成員函數,可以爲對象分配空間;對數據成員賦初值;請求其他資源。構造函數不光可以初始化對象,還可以進行其他資源的初始化。
關於構造函數的使用:

  • 構造函數名與類名相同:類名
  • 構造函數可以重載(構造函數一定重載!!)
  • 構造函數可以有任意類型的參數,但沒有返回類型

用戶沒有定義的構造函數時,系統自動提供缺省版本的構造函數,而如果我們定義構造函數,系統則不會主動生成默認構造函數,當我們定義的對象沒有給參數時是非常危險的。所以我們如果定義構造函數,最少要定義兩個,一個帶參,一個不帶參。

默認值

若所有參數都有默認值,則不加無參構造函數。函數形參的默認值要放到最後面,否則會產生二義性

析構函數(系統自動調用)

析構函數是用於取消對象的成員函數,當一個對象作用結束時,系統自動調用析構函數,析構函數的作用是進行對象消亡時的清理工作。析構函數不能重載
析構函數的使用:
析構函數名爲: ~ 類名
析構函數沒有參數,也沒有返回類型
一般情況下,可以不定義析構函數
但如果類的數據成員中包含指針變量是從堆上進行存儲空間分配的話,需要在析構函數中進行存儲空間的回收
另外注意:
刪除數組要加[]!否則只釋放了首地址的空間

delete[] name;

初始化列表(系統自動調用)

構造函數初始化成員有兩種方法:

  • 使用構造函數的函數體進行初始化
  • 使用構造函數的初始化列表進行初始化
funname(參數列表):成員名1(形參名1),成員名2(形參名2),成員名n(形參名n)
{  函數體,可以是空函數體  } 

!!必須使用初始化列表的幾種情況:
1、數據成員爲常量
2、數據成員爲引用類型
3、數據成員爲沒有無參構造函數的類的對象

另外需要注意:
類成員的初始化的順序:按照數據成員在類中的聲明順序進行初始化,與初始化成員列表中出現的順序無關

this指針

this指針也就是此時本對象的地址。

需要顯式引用this指針的三種情況
(1)在類的非靜態成員函數中返回類對象本身或對象的引用的時候,直接使用 return *this,返回本對象的地址時,return this。
(2)當參數與成員變量名相同時,如this->x = x,不能寫成x = x。
(3)避免對同一對象進行賦值操作,判斷兩個對象是否相同時,使用this指針。

複製構造函數

複製構造函數用一個已有同類對象創建新對象進行數據初始化
C++爲類提供默認版本的複製構造函數

類名 :: 類名(const  類名  &  引用名  ,  …);

注意定義時的const限定。引用類型防止丟失,const限定防止修改

以下三種情況下由編譯系統自動調用:

  • 聲明語句中用類的一個已知對象初始化該類的另一個對象時。
  • 當對象作爲一個函數實參傳遞給函數的形參時,需要將實參對象去初始化形參對象時,需要調用複製構造函數。
  • 當對象是函數的返回值時,由於需要生成一個臨時對象作爲函數返回結果,系統需要將臨時對象的值初始化另一個對象,需要調用複製構造函數。

深複製、淺複製

在用一個對象初始化另一個對象時,只複製了數據成員,而沒有複製資源,使兩個對象同時指向了同一資源的複製方式稱爲淺複製。
即:對於複雜類型的數據成員只複製了存儲地址而沒有複製存儲內容,默認複製構造函數所進行的是簡單數據複製,即淺複製

關於深複製:
通過一個對象初始化另一個對象時,不僅複製了數據成員,也複製了資源的複製方式稱爲深複製。
自定義複製構造函數所進行的複製是淺複製。

簡單來說,深複製不僅複製了對象的值,還複製了對象的資源,通俗點,深複製把人家原來對象的根全刨了,全部連根都拷貝到自己這來,最終結果深複製的對象和被複制的對象是完全一樣的。
而淺複製就不一樣了,淺複製只複製對象的值,沒有複製資源。
對於一些簡單的數據類型或者說沒有涉及到更深層次的操作時,深複製和淺複製並沒有太大的區別,甚至深複製更麻煩一些,但是如果在軟件運行時真正出了問題,那就會直接找不到資源。
舉個例子,比如複製數組,深複製是複製前後一共有兩個數組,而淺複製由於只複製值,所以僅僅是將被複制的數組首地址賦給了該數組,導致最後還是一個數組屬於兩個對象,上面提到了,對象的數據空間要求單獨、獨立,這樣顯然不符合。另外可怕的是,萬一被複制的數組改了儲存位置或者這個數組被刪除,那麼另一個對象對會找不到資源,這是極其危險的。
所以以後寫代碼最好還是不要怕麻煩,直接深複製,免除後患。
另外注意,string類型封裝了深複製,直接賦值就是深複製。

深複製的做法:對複雜類型的成員變量,使用new操作符進行空間的申請(真正有了自己的空間),然後進行相關的複製操作。

常成員

  • 常數據成員是指數據成員在實例化被初始化後,其值不能改變。
    如果在一個類中說明了常數據成員,那麼構造函數就只能通過初始化列表對該數據成員進行初始化,而任何其他函數都不能對該成員賦值。
  • 類的成員函數說明後面可以加const關鍵字,則該成員函數成爲常量成員函數。
    類型說明符 函數名(參數表) const;
    這裏要注意const的位置,const放到最前面表示返回值是常量,而放到最後則表示是常成員函數。
    常成員函數只能使用成員值但是不能修改。
    常成員函數不能更新對象的數據! 但是可以用於輸出
  • 如果在說明對象時用const修飾,則被說明的對象爲常對象。在定義常對象時必須進行初始化,而且不能被更新。常對象只能調用它的常成員函數、靜態成員函數、構造函數(具有公有訪問權限)

靜態成員

靜態數據成員爲同類對象共享。靜態成員函數與靜態數據成員協同操作。
靜態成員不屬於某一個單獨的對象,而是爲類的所有對象所共有。而對象的數據成員空間是獨立的,如下圖。
在這裏插入圖片描述
靜態成員函數的作用不是爲了對象之間的溝通,而是爲了能處理靜態數據成員: 保證在不依賴於某個對象的情況下,訪問靜態數據成員。
對於類的普通數據成員,每一個對象都各自擁有一個副本。(分配不同的存儲空間)
對於靜態數據成員,每個類都擁有一個副本 。(在靜態存儲區分配一個存儲空間,對所有對象都是可見的)

類成員僅有一個空間,也就是就算是被繼承了,那麼在子類裏訪問的也是原始類的類成員所在的空間。子類修改繼承父類的成員時,父類的成員同步修改。也就是,某個靜態成員被所有對象和所有派生類共同擁有一個空間。或者說任何基類和派生類的對象都可以修改該基類的靜態成員

公有訪問權限的靜態成員,可以通過下面的形式進行訪問

  • 類名::靜態成員的名字
  • 對象名.靜態成員名字
  • 對象指針->靜態成員的名字

在靜態成員函數內部,直接訪問。
注意:靜態成員函數不訪問類中的非靜態數據成員。如有需要,只能通過對象名(或指向對象的指針)訪問該對象的非靜態成員。

友元

友元函數會破壞類的封裝性,只有在運算符重載時纔會使用,其他時候不要使用。
如果在本類(類A)以外的其他地方定義了一個函數(函數B)在類體中用friend對其(函數B)進行聲明,此函數就稱爲本類(類A)的友元函數。
友元函數(函數B)可以訪問這個類(類A)中的私有成員。友元類和友元函數類似,只是擴大了開放範圍了,也就是若F類是A類的友元類,則F類的所有成員函數都是A類的友元函數,所有成員函數都可以訪問A類。

類的包含

類的包含是程序設計中一種軟件重用技術。即定義一個新的類時,通過編譯器把另一個類 “抄”進來。
當一個類中含有已經定義的類類型成員,帶參數的構造函數對數據成員初始化,須使用初始化語法形式。建立一個類的對象時,==要先執行成員對象自己的構造函數,再執行當前類的構造函數。==成員對象的構造函數調用次序和成員對象在類中的說明次序一致(聲明順序爲:a1、b1、b2),與它們在成員初始化列表中出現的次序無關(初始化列表順序爲:b1、b2、a1)。
析構函數的調用順序相反

構造函數 ( 形參表 ) : 對象成員1(形參表 ) ,, 對象成員n (形參表 )

運算符重載

重載運算符函數可以對運算符作出新的解釋,但原有基本語義不變,也就是可以對原有運算操作針對特對的類對象進行拓展,而不能改變原有的基本運算規則。
運算符函數可以重載爲成員函數或友元函數
其實質還是一個重載函數

其區別:
(1) 成員運算符函數比友元運算符函數少帶一個參數(後置的++、–需要增加一個形參)。
(2) 雙目運算符一般可以被重載爲友元運算符函數或成員運算符函數,但當操作數類型不相同時,必須使用友元函數。

->重載爲成員函數,解釋爲:
ObjectL . operator op ( ObjectR )

左操作數由ObjectL通過this指針傳遞,右操作數由參數ObjectR傳遞 

->重載爲友元函數,解釋爲:
operator op ( ObjectL, ObjectR )

左右操作數都由參數傳遞 ,常用於運算符的左右操作數類型不同的情況

透過參數的情況就可以看出,重載爲友元函數更爲靈活,因爲前者的重載必須需要ObjectL爲本類的對象,而當涉及到其他類對象時會出現錯誤(如複數的計算),所以這種情況不得不採用後者。

C++中不能用友元函數重載的運算符有

= (賦值) ()(調用函數運算符) [] (取元素運算符) ->(訪問成員)

上述運算符要想重載只能採用成員函數的形式。

而輸入輸出流必須要重載成友元函數。

學習感受

見證歷史的我們線上學習一個余月來,個人感覺和學校學習區別不大,反而感覺在家裏學習有更好的環境,前提是有自控力的約束下,效率比在學校高得多。也有了更多的時間自己研究代碼,深究其道理,發現程序語言的神奇與巧妙。從學過java語言、類和對象基本思想的角度來說,學習這一部分的理解更深一些,感觸更多一些,學習類和對象這一部分也是深有感觸,正如前面所說,類和對象這一部分正是c++拓展其數據表示形式的一部分,但是在擴展的過程中遇到了種種問題,而我們本專題的學習正是本着這種遇到問題——解決問題的思路的形式解決的。
本學期算是剛開始接觸c++的靈魂,所以現在在編寫代碼以軟件系統的開發爲主,學習軟件開發的基本思路。這部分系統開發無外乎增刪查改這幾部分。對於類這一部分,軟件開發的類大概定義爲兩大類,一類數據類,對應於數據庫,另一類是操作類,對應功能的具體實現。每個類必須要有的:至少兩個構造函數(一個帶參、一個無參)、get、set、展示函數(一定程度起到調試的目的)。在數據類還根據情況進行重載。操作類是實現功能的。調試時注意要一個一個函數分塊的調試,方便找錯。最好先寫add函數和展示函數,測試一下,沒問題開始分塊寫函數,每寫好一個函數就跟着一個main函數測試一遍,沒問題之後再繼續寫。要養成好的編程習慣,常常會事半功倍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章