c是一門面向過程的語言關注的過程,c++做爲一門面向對象的語言關注的時對象,把事情拆分爲不一樣的對象,經過對象的交互來實現。c++
1.類的引入
類用關鍵字class引出,與以前的結構體相似,不一樣的是結構體內不能定義函數而在類中能夠定義函數啦(c++中結構體也能夠定義函數哦)
2.類的定義
類中成員函數的兩種定義方式:能夠在類中定義定義,但編譯器有可能將其看成內聯函數來處理故推薦第二種——將函數的聲明和定義分開。
3.類的訪問限定符
public、protected、private--->protected和private修飾的對象在類外不能直接訪問
class類默認是private,struct默認是public
面向對象的三大特性:封裝、繼承、多態,這裏咱們說一下封裝的定義:將數據和操做數據的方法進行有機結合,隱藏對象的屬性和實現細節,僅對外公開接口來和對象進行交互。
4.類的做用域
類定義了一個新的做用域,若要訪問類中的成員要用做用域限定符::進行訪問。
在這裏咱們已經接觸了四個做用域:全局做用域、函數內的局部做用域、類、命名空間。
5.類的實例化(對象)
類限定了有那些成員,類是沒有空間開闢的,用類建立出的對象纔有實際的內存空間。
到這裏咱們要思考一個問題,一個類的大小該如何計算呢??類中能夠包含成員函數,此時又該怎麼辦呢??
經過測試咱們能夠知道類的大小其實就是類的成員變量的大小,類中的成員函數被放在公共代碼區,要注意內存對齊的問題。若是是一個空類,或這個類中只有成員函數,呢麼這個類的大小是1,由於建立對象時要有區分。
6.this指針
c++對每一個成員函數增長了一個默認的指針參數,讓該指針指向當前對象(函數運行時調用該函數的對象),在函數體中全部成員變量的操做,都是經過該指針去訪問。只不過全部的操做對用戶是透明的,即用戶不須要來傳遞,編譯器自動完成。呢麼這個就至關於咱們用戶本身在傳參時傳入地址,正由於有this指針的存在因此函數才能準確的訪問到調用它的主體。ide
一個類定義好的話裏面並非什麼都沒有,會自動生成六個默認函數
1.構造函數
構造函數是幹什麼的呢?
構造函數是一個特殊的成員函數,名字與類名相同,建立類類型對象時由編譯器自動調用,保證每一個數據成員都有 一個合適的初始值,而且在對象的生命週期內只調用一次。也就是說構造函數完成了對象的初始化,它的特色是:函數名與類名相同、無返回值、對象實例化時編譯器自動調用對應的構造函數、構造函數能夠重載。系統自動生成的是無參的構造函數,固然咱們能夠本身寫一個帶有參數的構造函數,那麼咱們在定義對象的時候就能夠直接初始化拉,看下面的代碼:class data<br/>{<br/>public:<br/>data(int year=1999,int month=1,int day=1)<br/>{<br/>_year = year;<br/>_month = month;<br/>_day = day;<br/>}<br/>private:<br/>int _year;<br/>int _month;<br/>int _day;<br/>};
函數
那麼在這個地方咱們就完成了構造函數的建立,呢麼在主函數內定義對象的時候就能夠直接初始化對象:
data a (1999,10,13);測試
那麼咱們知道系統給的是無參的構造函數,因此咱們在定義無參構造函數的時候就不須要帶(),不然就變成了函數聲明。
上述的代碼就是咱們顯式建立的構造函數,當有它存在的時候編譯器就不會再生成默認構造函數this
還有一個注意的點:無參構造函數和全缺省構造函數都稱做默認函數,是不容許重複定義的,只容許存在一個,故這兩個是不能構成重載函數的。指針
呢麼構造函數的做用是什麼呢?-->由於變量類型不僅是固定的int或char這種,也有多是用戶自定義類型,那麼構造函數就會調用這種自定義類型的成員函數,這就是它的做用。code
2。構造函數賦初值
上述過程當中在構造函數體內的操做並非初始化,那麼咱們習慣將用構造函數的初始化列表對其進行初始化。對象
初始化列表:以一個冒號開始,接着是一個以逗號分隔的數據成員列表,每一個"成員變量"後面跟一個放在括號中的初始值或表達式。
public:
data(int year=1999,int month=1,int day=1)
:_year(year)
, _month(month)
, _day(day)
{繼承
}
注意:1.就算沒有初始化列表系統也會自動生成一個初始化列表,每一個變量只能初始化一次,並且當成員變量是引用類型、const修飾、類類型成員變量(沒有默認構造函數)這三種狀況時必須使用初始化列表對其初始化,當一個沒有默認構造函數的類類型成員變量不初始化系統也不會自動識別該如何初始化,故會出錯。遞歸
2.成員變量的初始化順序是按照成員變量的聲明順序決定的,與初始化列表的順序無關,那麼咱們在初始化列表儘可能於聲明的順序相同,更不要用變量進行賦值,極容易出錯。
3.static成員
在c++中static能夠修飾成員變量和成員函數,由static修飾的變量或函數稱做靜態成員變量或靜態成員函數
static修飾的爲全部類對象共享的!
區別:
| 靜態成員變量 | 普通成員變量 |
| 初始化在類外 | 類內初始化 |
| 全部對象共享的 | 每一個對象都包含了一份 |
| 也能夠經過類名::靜態成員名訪問 | 只能經過對象訪問 |
| 全部對象共享的 | 每一個對象都包含了一份 |
靜態成員變量不會影響類的大小,也就是計算類的大小時不須要計算靜態成員的大小,他們用的是同一塊內存空間
| 靜態成員函數 | 普通成員函數 |
| 沒有隱藏的this指針 (不能訪問非靜態成員) | 有this指針 |
| 不能被const修飾 (由於const修飾成員函數實際上修飾的時this指針) | 能夠 |
| 能夠不經過對象調用 | 必須經過對象調用 |
用static修飾的靜態成員函數只能訪問靜態成員變量,不能訪問普通成員變量。
而普通成員函數均可以訪問。
析構函數與構造函數函數相對應,和構造函數構成函數重載,也就是說函數名也是類名,在函數名前加~表明析構函數,這個函數沒有參數和返回值,析構函數並非對類對象進行銷燬,而是完成類的一些資源清理工做。對象在生命週期結束時會自動調用析構函數。
拷貝構造函數:只有單個形參,該形參是對本類類型對象的引用(通常經常使用const修飾),在用已存在的類類型對象建立新對象時由編譯器自動調用。
注意:拷貝函數是對構造函數的一個重載,它的形參只有一個且必須船引用,若是傳值調用會無窮遞歸。
那麼有這個函數的存在咱們在定義對象時就能夠用已有的對象進行新對象的定義:
如:date d1; data d2(d1);
一樣的,編譯器也會爲咱們生成一個默認的拷貝構造函數,但它完成的時淺拷貝工做,好比下面的狀況:
class String { public: String(const char* str = "jack") { _str = (char*)malloc(strlen(str) + 1); strcpy(_str, str); } ~String() { cout << "~String()" << endl; free(_str); } private: char* _str; }; int main() { String s1("hello"); String s2(s1); }
在構造函數內咱們完成了動態建立內存空間,這種狀況默認拷貝構造函數就會原封不動的把這塊地址拷貝到新對象d2中,這並非咱們想要的結果,d2和d1共用同一塊內存空間對d2進行修改d1也會改變。這叫作淺拷貝,因此咱們有必要自定義一種深拷貝的構造函數。這個後期在作介紹。
在談賦值運算符重載前咱們有必要先談一下什麼是運算符重載,那麼兩個類是沒法比較大小的,可是若是咱們進行運算符重載,按照大小關係進行重載那麼類也就可使用比較運算符了,其它運算符也是同樣的。
用關鍵字operator進行運算符重載
有一類特殊的運算符重載,就是前置++和後置++的重載,爲了有所區別
賦值運算符主要有四點:
一樣完成的也是淺拷貝,因此咱們也有必要本身實現一個賦值運算符的重載。深拷貝的實現一樣也放在後面說。
最後兩種默認函數是普通變量和const變量的取地址,這兩種不多本身實現。
若是咱們對輸出運算符「<<」進行重載該怎麼作呢
ostream & operator<<(ostream & _cout)------>這是對<<重載的函數函數,若是這樣定義函數的話其中包含的this指針做爲第一個形參,因此在調用函數想要輸出一個類的時候就不能按照常規的cout<<d這種方式進行輸出,函數的第一個參數是this指針,因此調用時要d<<cout,這樣才能正常的輸出。
那麼咱們怎麼能避免這種方式而是按照常規的方式對<<進行重載呢
這裏就要咱們就要在全局做用域定義一個重載函數把兩個參數按照正常的順序傳入,可是在類中就不能不能引用這個函數的私有成員,咱們只要在類中聲明這個函數用friend關鍵字修飾那麼這個函數就是這個類的友元函數也就能夠在類內訪問了
注意:
友元函數是普通函數並非類中的成員函數
友元函數可訪問類的私有成員,但不是類的成員函數
友元函數不能用const修飾(由於const修飾的是this指針,而友元函數並無this指針)
友元函數能夠在類定義的任何地方聲明,不受類訪問限定符限制
一個函數能夠是多個類的友元函數
友元函數的調用與普通函數的調用和原理相同
一樣一個類也能夠是另外一個類的友元類,這個類就能夠訪問其內部的私有成員了,但注意此過程是單向的且不能傳遞。