標籤:c++ stl c++ primer 標準模板庫
C++ Primer 學習筆記_15_類與資料抽象(1)_類的定義和聲明
在C++中,用類來定義自己的抽象資料類型。通過定義類型來對應所要解決的問題中的各種概念,可以使我們更容易編寫、調試和修改程式。可以使得自己定義的資料類型用起來與內建類型一樣容易和直觀。
看一下Sales_item類:
class Sales_item{private: std::string isbn; unsigned units_sold; double revenue; public: double ave_price() const; bool same_isbn(constSales_item &rhs) const { return isbn ==rhs.isbn; } Sales_item():units_sold(0),revenue(0) {}}; double Sales_item::ave_price() const{ if (!units_sold) { return 0; } return revenue /units_sold;}
一、類定義:扼要重述
簡單來說,類就是定義了一個新的類型和一個新的範圍。
1、類成員
一個類可以包含若干公有的、私人的和受保護的部分。我們已經使用過public 和private訪問標號:在public部分定義的成員可被使用該類型的所有代碼訪問:在private部分定義的成員可被其他類成員訪問。
所有成員必須在類的內部聲明,一旦類定義完成之後,就沒有任何方式可以增加成員了。
2、建構函式
在建立一個類對象時,編譯器會自動使用一個建構函式來初始化該對象,也就是說:建構函式用於給每個資料成員設定適當的初始值。
建構函式一般使用一個建構函式初始化列表,來初始化對象的資料成員。建構函式初始化類表由成員名和帶括弧的初始值組成,跟在建構函式的形參表之後,並以冒號(:)開頭。
比如:Sales_item():units_sold(0),revenue(0){}
3、成員函數
在類內部定義的函數預設為內嵌函式(inline)。
而在類的外部定義的函數必須指明它們是在類的範圍中。
就const關鍵字加在形參表之後,就可以將成員函式宣告為常量,如:double avg_price() const;
const必須同時出現在聲明和定義中,若只出現在一處,則會出現編譯時間錯誤!
//類內double avg_price() const;//類外double Sales_item::ave_price() //error{ if (!units_sold) { return 0; } return revenue /units_sold;}
二、資料抽象和封裝
資料抽象是一種依賴於介面和實現分離的編程技術:類的設計者必須關心類是如何?的,但使用該類的程式員不必瞭解這些細節。
封裝是一項將低層次的元素組合起來形成新的、高層次實體的技術!
1、訪問標號實施抽象和封裝
1)程式的所有部分都可以訪問帶有public標號的成員。類型的資料抽象視圖由其public成員定義。
2)使用類的代碼不可以訪問帶有private標號的成員。private封裝了類型的實現細節。
【class與struct的區別】
class預設的是私人的,struct預設是公有的!
2、編程角色的不同類別
好的類的設計者會定義直觀和易用的類介面,而使用者(此處可以指程式員)則只需關心類中影響他們使用的那部分實現。
注意,C++程式員經常會將應用程式的使用者和類的使用者都稱為“使用者”。
【關鍵概念:資料抽象和封裝的好處】
1)避免類內部出現無意的、可能破壞對象狀態的使用者級錯誤。
2)隨著時間的推移,可以根據需求改變或缺陷報告來完善類實現,而無需改變使用者級代碼。
【註解】
改變標頭檔中的類定義可有效地改變包含該標頭檔的每個源檔案的程式文本,所以,當類發生改變時,使用該類的代碼必須重新編譯。
三、關於類定義的更多內容
1、同一類型的多個資料成員
普通的聲明:
class Screen{public: //... private: std::string contents; std::string::size_type cursor; std::string::size_typeheight,width;};
2、使用類型別名來簡化類
出了定義資料和函數成員之外,類還可以定義自己的局部類型名字。如果為std::string::size_type提供一個類型別名,那麼Screen類將是一個更好的抽象(將index定義放在public部分):
class Screen{public: //... typedef std::string::size_type index; private: std::string contents; index cursor; index height,width;};
3、成員函數可以被重載
成員函數只能重載本類的其他成員函數。
重載的成員函數和普通函數應用相同的規則:兩個重載成員的形參數量和類型不能完全相同。調用非成員重載函數所用到的函數匹配過程也應用於重載成員函數的調用。
4、定義重載函數
class Screen{public: typedef std::string::size_type index; //重載函數get char get() const { return contents[cursor]; } char get(index ht,index wd) const; private: std::string contents; index cursor; index height,width;};
5、顯式指定inline函數
在類內部定義的成員函數,預設就是inline函數。但是也可以顯式的將成員函數指定為inline:
class Screen{public: typedef std::string::size_type index; //類內的函數預設就是inline函數 char get() const { return contents[cursor]; } inline char get(index ht,index wd) const; index get_cursor() const; private: std::string contents; index cursor; index height,width;}; //已經在類體中聲明為inline了,就沒必要再次聲明char Screen::get(index r,index c)const{ index row = r * width; return contents[row + c];} //即使沒有在類體中聲明,也可以在外面補上inline Screen::indexScreen::get_cursor() const{ return cursor;}
在聲明和定義處指定inline都是合法的。在類的外部定義 inline 的一個好處是可以使得類比較容易閱讀。
【註解】
像其他inline一樣,inline成員函數的定義必須在調用該函數的每個源檔案中是可見的。不在類定義體內定義的inline成員函數,其定義通常應放在有類定義的同一標頭檔中。
四、類聲明和類定義
在一個給定的源檔案中,一個類只能被定義一次。如果在多個檔案中定義一個類,那麼每個檔案中的定義必須是完全相同的。
可以聲明一個類而不定義它:
//前向聲明class Screen;
在聲明之後、定義之前,類Screen是一個不完全類型,即:已知Screen是一個類型,但不知道包含哪些成員。
不完全類型(incompletetype)只能以有限方式使用。不能定義該類型的對象。不完全類型只能用於定義指向該類型的指標及引用,或者用於聲明(而不是定義)使用該類型作為形參類型或傳回型別的函數。
在建立類的對象之前,必須完整的定義該類。必須是定義類,而不是聲明類,這樣,編譯器就會給類的對象預定相應的儲存空間。同樣的,在使用引用或指標訪問類的成員之前,必須已經定義類。
1、為類的成員使用類聲明:
只有當類定義已經在前面出現過,資料成員才能被指定為該類類型。如果該類型是不完全類型,那麼資料成員只能是指向該類類型的指標或引用。
因為只有當類定義體完成後才能定義類,因此類不能具有自身類型的資料成員。然而,只要類名一出現就可以認為該類已聲明。因此,類的資料成員可以是指向自身類型的指標或引用:
class LinkScreen
{
Screen Window;
LinkScreen *next;
LinkScreen *prev;
};
五、類對象
定義一個類時,也就是定義了一個類型。一旦定義了類,就可以定義該類型的對象。定義對象時,將為其分配記憶體空間,但(一般而言)定義類型時不進行儲存分配。一旦定義了對象,編譯器則為其分配足夠容納一個類對象的儲存空間
每個對象具有自己的類資料成員的副本。修改其中一個對象不會改變其他該類對象的資料成員。
1、定義類類型的對象
定義了一個類類型之後,可以按以下兩種方式使用。
1)將類的名字直接用作類型名。
2)指定關鍵字class或struct,後面跟著類的名字:
Screen scr; //兩條語句作用相同 class Screen scr;
2、為什麼類的定義以分號結束
因為在類定義之後可以接一個對象定義列表,所以,定義必須以分號結束,可以給一個《實踐:求主要元素》
//vs2012測試代碼#include<iostream>#include<vector> usingnamespace std; #definen 5 //方法一:每找出兩個不同的element,就成對刪除即count--,最終剩下的一定就是所求的。時間複雜度:O(n)classSolution {public: int majorityElement(vector<int>&num) { int element; int length = num.size(); int count = 0; for(int i = 0; i < length; i++) { if(count == 0) { element = num[i]; count = 1; } else { if(num[i] ==element) count++; else count--; } } count = 0; for(int i = 0; i < length; i++) { if(num[i] == element) count++; } if(count > length / 2) cout << element <<endl; else cout << "can not findthe majority element" << endl; return 0; }}lin, lin2; int main(){ int a; vector<int> num; for(int i=0; i<n; i++) { cin>>a; num.push_back(a); } lin.majorityElement(num);}
運行結果:
輸入:23 2 3 2
2
【註解】
通常,將對象定義成類定義的一部分是個壞主意!!!這樣做,會使所發生的操作難以理解。對讀者而言,將兩個不同的實體(類和變數)組合在一個語句中,也會令人迷惑不解。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
C++ Primer 學習筆記_15_類與資料抽象(1)_類的定義和聲明