前言:c++
找工做須要,最近看了下一些C++的基本概念,爲範磊的《零起點學通C++》,如下是一些筆記。數組
內容:app
delete p;只是刪除指針p指向內存區,並非刪除指針p,因此p仍是能夠用的。刪除空指針所指向內存是能夠的。函數
堆中的變量和對象時匿名的,沒有名稱,只能經過指針來訪問。this
在堆中建立對象時,在分配內存的同時會調用類的構造函數,在刪除堆中對象時,會調用類的析構函數。spa
爲了不內存泄露,在刪除一個指針後應該將其其值賦爲0。指針
常量指針是指針指向的內存區域地址不能改變,可是該內存地址裏保存的值是能夠改變的,好比int a; int * const p = &a;對象
指向常量的指針表示指針指向的對象是不能被修改的,可是該指針能夠被修改,即該指針能夠指向另外一塊目標內存地址。好比const int a = 0; const int *p = &a; 若是A是一個類,也能夠爲const A* p = new A;繼承
而指向常量的常指針表示指針自己不能被修改,其指向的內存地址內容也不能被修改。好比const int a = 0; const int * const p = &a;生命週期
引用就是別名常量。
堆中的地址是用指針來操做的,用不到別名。
若是在main函數前面的其它函數的聲明和定義是一塊兒的,則代表這個函數是內聯函數!所以當該函數較長時,應該將聲明和定義分開。
能夠經過指針或引用來返回多個值,由於返回機制只能返回一個值,因此其它的返回值當作參數來傳入,經過引用或指針。若是按值傳遞時,對象很大,則系統開銷會很大(好比對象傳入和返回,要建立2次臨時對象,所以也會屢次調用構造函數和析構函數),此時,通常採用按地址傳遞或按引用傳遞。
既然引用實現了指針的功能,並且使用起來更加方便,爲何還要指針呢?
這是由於指針能夠爲空,可是引用不能爲空,指針能夠被賦值,可是引用只能夠被初始化,不能夠被賦爲另外一個對象的別名。若是你想使用一個變量來記錄不一樣對象的地址,那麼就必須使用指針,另外指針也能夠是多重的。
指針能夠指向堆中空間,引用不能指向堆中空間。但指針和引用能夠一同使用,另外要注意引用的生命週期。
對於引用而言,若是引用是一個臨時變量,那麼這個臨時變量的生存週期不會小於這個引用的生存期。指針不具有這個特性。
爲了不內存泄露,咱們不能按值的方式返回一個堆中對象,而必須按地址或者別名的方式返回一個別名或者內存地址,這樣就不會調用複製構造函數來建立一個該對象的副本了,而是直接將該對象的別名或者地址返回。
爲了不指針混淆,咱們必須對堆中的內存在哪一個函數中建立,就在哪一個函數中釋放。
若是函數名相同,函數參數的個數也相同,只是參數的類型不一樣,則也能夠是函數重載。
若是函數有缺省值,則在調用時該參數能夠不傳入,這樣至關於函數個數少了。
默認參數其實也能夠看作是一種函數重載,但默認參數的函數若是不加標註的話很容易被忽略,並且容易被有參數的同名函數覆蓋。而一般的重載函數使用方便,易於理解。具備默認參數的重載的是參數的數值,而重載函數重載的是參數的類型。
我能夠對構造函數的函數頭對常量和引用進行初始化,此時的初始化順序是按照成員列表的順序進行的,而不是按構造函數頭賦值的順序。而析構順序剛好相反。
通常狀況下,編譯器會自動爲類生成一個默認的複製構造函數。
清楚構造函數和new的結合,析構函數和delete的結合。
構造函數是不能設置爲私有的。
默認的構造函數是淺層的構造函數,若是類中的成員變量有指針的話,就頗有可能出現內存泄露的錯誤,所以此時須要使用深層的構造函數。
用已有對象來建立對象時,纔會調用複製構造函數。若是複製符左側是已有對象,右側也是已有對象,則不會調用複製構造函數,而是調用一個賦值運算符函數。
只要建立一個類,編譯器就會自動添加4個函數:構造函數,析構函數,複製構造函數,賦值運算符函數,其中系統默認的複製構造函數和複製運算符都屬於淺層拷貝。
默認的自加劇載運算符爲前置自加運算符。
能夠經過建立一個無名的臨時對象來完成重載自加後的對象賦值操做。固然了,較好的方法是返回*this指針,這樣就不須要創建一個臨時對象了,不過它依舊會調用賦值運算符,爲了不這種狀況,能夠將重載自加函數返回值類型設爲返回對象的引用。
重載後置自加運算符時,爲了區分與前置自加的區別,須要添加一個毫無心義的參數,這個參數只是在編譯器中使用到,在函數體中沒用。
在一條語句中自加運算符的執行順序爲從右向左,且不一樣的編譯器入棧的順序不一樣,好比說VC6.0中其入棧是要求該語句的表達式執行完後。所以a=1;cout<<a++<<++a;則分別輸出的是2,2;
++的結合性爲右結合,因此++++i合法,而i++++不合法,能夠改成(i++)++;
函數的按值返回時,會創建一個臨時的對象,返回完後隨後又被銷燬。
在符合隱式類型轉換的狀況下,能夠將一個變量賦值給一個對象,可是不能反過來(除非本身重寫重載函數)。
重載類型轉換運算符是沒有返回值的,和構造函數,析構函數同樣。
只有派生類對象能夠賦值給基類對象,反過來是不行的。另外,基類指針和引用能夠指向派生類,反過來也是不行的。
多重繼承容許分別設置基類的派生權限。
單一繼承中構造與析構的順序時,先構造的對象後析構。若是是多重繼承,則構對象函數的順序按照繼承列表中的順序來。
多重繼承中,若是多個基類中的函數名相同,則在子類對象調用該函數時會產生混淆,此時應該加入類做用域符。
當咱們在子類中定義一個與基類相同的同名函數時,那麼等於告訴編譯器,用子類的函數覆蓋掉基類的同名函數,同時將基類的重載函數給隱藏起來了,此時子類對象中不能再調用父類的對應的重載函數了。
const對象只能調用const成員函數。
若是一個派生類從多個基類派生,而這些基類又有一個共同的基類,那麼在派生類中訪問共同基類中的成員函數時會產生二義性,解決方法是須要指定類的做用域,或者將共同的基類設置爲虛基類,由於虛基類不會產生二義性。
一個虛函數被說明爲虛函數,在派生類中覆蓋了該函數,那麼該函數也是個虛函數,不過你也能夠在它前面添加關鍵字virtual,這樣更容易理解。
繼承所體現出的不一樣子類相同函數有不一樣的表現,可是這個並不能體現類的多態性,由於類的多態性要求能夠用基類指針來操做子類。
將一個調用函數聯接上被正確調用的函數,這一過程叫作函數聯編,通常簡稱爲聯編,聯編分爲靜態聯編和動態聯編2種。
須要分清楚在編譯時的靜態聯編,在運行時的靜態聯編,在編譯時的動態聯編,在運行時的動態聯編這幾種狀況。
假如咱們在虛函數中沒有采用指針或引用,那麼就沒法實現動態聯編。
三種調用虛函數的方式,即按值調用,按指針調用和按引用調用,其中只有按值調用不能體現類的多態性,其它2種均可以。
在虛函數中可使用成員名限定強行解除動態聯編。
若是基類中定義了虛函數,析構函數也應該說明爲虛函數。這樣對內存的回收會更準確些。基類是虛析構函數時,若是派生類被銷燬,則首先是調用派生類的析構函數,而後纔是基類的析構函數。
數組一般比較大,因此爲了節省內存,C++規定數組在程序只有一個本來,因爲這個緣由,數組在函數中是不可能在創造一個副本的。C++爲了追求程序的運行速度,不對數組進行越界檢查。
若是a表示的是數組指針,delete[] a,表示刪除堆中的指針數組a,而delete a,表示刪除堆中的指針數組第一個指針。
用cin輸入字符時,會把空格當作是字符串的結束字符,所以能夠採用gets()函數。或者使用cin.get()函數,該函數有2個參數,第一個是數組,第二個是字符串的大小。
在重載下標運算符函數時應該注意:1.因爲函數的參數便是數組的下標,所以該函數只能帶一個參數,不可帶多個參數。2.因爲下標運算符只限於本類的對象使用,所以不得將下標運算符函數重載爲友元函數,且要求是非靜態類的成員函數。
因爲malloc和free函數產生於C語言時代,所以不可用在c++的對象中,由於對象的產生要調用構造函數。
dynamic_cast的做用是對不一樣類之間的數據類型進行轉換,它能夠將一個基類的指針轉換成一個派生類的指針。dynamic_cast屬於RTTI(運行時信息),須要在工程設置中將其啓動。在程序中應該少用RTTI.
若是使用指向基類的指針來指向派生類,那麼該指針不能直接調用派生類中多餘的成員函數(能夠經過強制類型轉換),除非該函數在基類中聲明成了虛函數。
由抽象類派生的類須要爲每個純虛函數賦予具體功能。抽象類不能用來定義抽象類對象,可是卻能夠定義一個指向抽象類的指針。
一個抽象基類仍然能夠派生出抽象類,只要該類沒有把純虛函數所有覆蓋掉。由於派生出的抽象類,若是其子類沒有將它的純虛函數所有覆蓋掉,那麼該子類也屬於抽象類。
在用單一繼承就能夠實現的狀況下,不要使用多重繼承。
靜態成員變量須要有定義和聲明兩個階段。且定義必須在全局定義,靜態成員在沒有對象以前已經存在了。
公有的靜態成員函數在程序中全部的函數都可以訪問它,即包括那些非類的成員函數。而私有靜態變量只能被類的成員函數調用,但前提是你必須定義一個對象。
靜態成員函數和靜態成員變量是同樣的,它們不單屬於一個對象,而是屬於整個類。靜態成員函數只能調用靜態成員變量,不能調用普通的成員變量。
雖然能夠經過對象來訪問靜態成員函數,可是最好是用類限定符來訪問它們。
基類和派生類均可以共享靜態成員。
類中任何成員函數均可以訪問靜態成員,可是靜態成員函數不能直接訪問非靜態成員,只能經過對象名訪問該對象的非靜態成員。由於靜態成員函數是屬於整個類的,沒有特定指向某個對象的this指針。
靜態成員函數不能被說明爲虛函數。
函數名是指向函數的第一條指令的常量指針,這與數組名是指向數組中第一個元素的常量指針同樣。
函數指針應該將函數名和前面的指針給括起來。
使用函數指針能夠減少一些重複的代碼,由於函數指針名能夠看做是函數名的代號,咱們能夠經過它來直接調用,函數指針常常會在條件或者判斷語句裏出現,以便於用戶選擇調用不一樣名字但又返回值類型和參數類型徹底相同的函數。
函數指針能夠做爲函數的參數傳入。
可使用typedef來簡化函數指針的聲明,後面以分號結束,注意這點和宏定義是不一樣的,也能夠在類中使用成員函數指針,或者成員函數指針數組。
空格的ascii碼爲32,而空字符的ascii碼爲0.
cout遇到空字符會中止輸出,但與cin不一樣的是,cout能夠輸出空格,製表符等空字符或者不可見字符。
若是char型數組後面沒有添加字符串結束符的話,這它不是一個字符串,而是一個數組。
strlen返回的是字符串結束標誌’\o’以前的字符串長度,而不是數組長度。sizeof返回的是數組的所佔的字節長度。
char字符串的比較只能用循環的方式進行(或者使用庫函數strcmp函數),而string的比較能夠直接用等號。
不能夠直接對char型字符串數組進行賦值操做,而只能使用strcpy函數,或者對數組每一個元素一個個的賦值。而string類的字符串能夠直接用等號進行賦值。另外,string類中還有一個專門的賦值函數assign,這個函數可指定賦值字符串起點和長度。
查看char型字符串的長度能夠用strlen函數。string中用size成員函數實現該功能。固然了string類中的length函數也是同樣的功能,二者的區別僅僅在size是爲了兼容STL而出現的,而length是早起的string計算長度的版本。
未被初始化的string對象是個空對象,除了字符串結束標誌外,沒有任何數據。
在char中,strncat能夠實現部分字符串的合併。在string中是append函數。同理,char中用strncpy來實現字符串的替換,在string中使用replace來實現該功能,且replace的重載函數中能夠兼容char型字符串數組。
char型字符串的拷貝用memmove, string中使用copy成員函數。
string中用insert來插入,其string類是從下標爲1開始數的。erase完成字符串的刪除。
char型字符串的查找使用函數strchr。而string類中使用find函數實現。該函數有不少變形的函數,好比find_first_not_of函數,表示找到第一個不相同的字符。同理還有find_first_of,find_last_of等函數。反向查找使用rfind()函數,可是它的返回值位置仍是從頭開始的,並非從末尾開始的。
string中用compare實現比較,它的c_str()返回一個指向char型的const指針。
char型字符串傳參數到函數中時(能夠經過指針或者字符串數組傳入),能夠不用傳入字符串的長度,由於它有結束符,能夠用來做爲其結束的標誌。
C++中結構體與類惟一不一樣的區別是,結構體的成員默認的是公有的,其它類有的,結構體也有,好比繼承,多態等。
爲了使計算機執行減法,採用了補碼的形式,由於引入了負號。
派生類的析構函數會自動調用基類的析構函數。
只要基類中的析構函數被說明爲虛函數,那麼派生類的析構函數不管說明與否,都天然是虛構函數。
友元函數並非類的成員函數,而是類的外部函數,可是該外部函數中類的對象能夠調用類的私有成員,而通常狀況下,類的對象時不能調用類的私有成員的。
常量對象只能調用可操做常量對象的成員函數(即參數括號後面帶有const的成員函數),而不是常量對象的成員函數只能被很是量對象調用,所以常量成員函數和很是量成員函數也算是一種函數重載形式。常量對象的私有數據成員是不可以被修改的,除非在構造函數中第一次修改。且const成員函數中不能調用非const函數。
友元類的做用不是相互的,須要單獨設置。
包含對象是將另外一個類的對象做爲該類的成員,而嵌套類是在該類中定義了一種新類型,這個類型只能在該類中使用。因爲嵌套類做爲一種自定義的數據類型被封裝在另外一個類中,所以可避免與其它類的名稱衝突。
參考資料:
《零起點學通C++》,範磊。