C++編譯器到達main()末尾時沒有遇到返回語句,則默認return 0;是結尾
返回值類型能夠是任何類型,除了數組,但能夠將數組做爲結構或對象組成部分返回git
顯示字符串時,在字符串中包含換行符n,可減小輸入量,可是endl確保程序繼續運行前刷新輸出(顯示在屏幕上),而n不保證。程序員
C++中回車、空格、製表符的做用相同。也就是說一般能夠在可以使用回車的地方使用空格,反之亦然。算法
Short至少16位
Int至少與short同樣長
Long至少32位,且至少與int同樣長
Long long至少64位,且至少與long同樣長
Float至少32位
Double至少48位,且很多於float
Long double至少和double同樣多express
<climits>中符號常量
CHAR_BIT char的位數
CHAR_MAX char的最大值
CHAR_MIN char的最小值
SCHAR_MAX signed char 的最大值
UCHAR_MAX unsigned char 的最大值
SHRT_MAX short的最大值
INT_MAX int的最大值
LONG_MAX long的最大值
ULLONG_MAX unsigned long long的最大值編程
若是知道變量的初值是什麼,則應當對它進行初始化。可避免之後忘記給它賦值的狀況發生,{ }內不包含任何東西,則變量將被初始化爲零
應當在聲明中對const進行初始化
若將非const地址賦給const指針,則可使用原地址改變const指針的數據,所以不容許。
若是條件容許,則應將指針形參指明爲指向const的指針數組
Strlen()返回字符串長度,而不是數組自己長度,而且只計算可見字符,不包括空字符
Getline()讀取一行輸入,直到換行符,(或者是讀取指定的字數)隨後getline()丟棄換行符,
Get()讀取一行輸入,直到換行符,將換行符保留在輸入序列中
Cin.get()不含參數時,可讀取下一個字符(即便是換行符)
cin讀取char值時忽略空格和換行符
cin.get(ch)讀取輸入中的下一個字符,(即便是空格)
測試條件能夠寫while( cin.get(ch) )表示讀取一個字符成功安全
對於枚舉enum只定義了賦值運算符,沒有算術運算
首個枚舉量默認爲0,後面沒有被初始化的枚舉量比前面的大1,能夠建立多個值相同的量
Enum bits {zero, null=0, one, numer0_uno=1};數據結構
Int p1,p2;建立一個int指針和一個int變量dom
Short (*ps)[20] = &tell;
Ps是一個指向包含20個元素的short數組的指針
Short *ps[20] = &ti;
Ps是一個short指針數組,包含20個元素ide
在C++中用引號括起的字符串像數組名同樣,也是第一個元素的地址
通常來講,給cout提供一個指針,它將打印地址。但若是指針類型爲char,則會打印其指向的字符串。若是要顯示字符串的地址,則必須強制轉換爲另外一種指針類型,如int
Vector效率比數組稍低,若是須要長度固定的數組,應使用array,效率相同更方便更安全
Vector<typename> vt(n_elem), array<typename, n_elem> arr;後者n_elem不能是變量
Vector是動態數組的替代品,array是定長數組的替代品
使用at()和[ ]的區別在於,at()將在運行期間捕獲非法索引,程序將默認中斷,但運行時間更長,
arr[i] == *(arr +i)
&arr[i] == arr + i
++n和n++對於內置類型,採用哪一種格式不會有差異,可是用戶定義的類型,若是有用戶定義的遞增和背叛運算符,則前綴的效率更高
{ }複合語句(代碼塊)中定義的新變量,僅當程序執行語句塊中的語句時才存在
C++規定,||和&&運算符是一個順序點(sequence point),即先修改左側的值,再斷定右側,若是左側爲true則不會去斷定右側的表達式
冒號和逗號也是順序點
<cctype>中的字符函數
Isalpha()
Isalnum(),字母或數字
isdigit(),數字
islower()
isupper()
ispunct()標點符號
a>b? c : d;
for循環中continue使程序跳到更新表達式處,而後跳到測試表達式處,
while循環中continue使程序直接跳到測試表達式處。
當用戶輸入錯誤時1重置cin以接受新輸入,2刪除錯誤輸入3提示再輸入
While(! (cin>>g[i]) ){
Cin.clear(); While(cin.get() != ‘\n’) Continue; Cout<<」please enter a number: 「;
}
檢查文件是否被成功打開的首先方法是使用方法is_open()
inFile.open(「bow.txt」);
if ( !inFile.is_open() ){
exit(EXIT_FAILURE);
}
exit()原型在頭文件<cstdlib>中定義
在C++中括號爲空與括號中使用void是等效的,意味着函數沒有參數
參數(argument)表示實參,參量(parameter)表示形參
遞歸方法有時被稱爲分而治之策略divide-and-conquer-strategy
使用typedef簡化函數指針類型
Typedef const double (p_fun)(const double*, int); //p_fun是別名
使用內聯函數一般作法是省略原型,將整個定義(即函數頭和全部函數代碼)放在本該提供原型的地方。內聯函數不能遞歸,只有在函數很短時才能採用內聯方式
返回引用時最重要的一點是,應避免返回函數終止時再也不存在的內存單元引用。最簡單的方法是返回一個做爲參數傳遞給函數的引用,另外一種方法是用new分配新的存儲空間並返回指向該內存空間的指針。不能夠返回指向局部變量或臨時對象的引用,由於函數執行完畢後,局部變量或臨時對象將消失,引用將指向不存在的數據。
四捨五入int(f +0.5)
引用變量是一種假裝指針,它容許爲變量建立別名,主要被用做處理結構和類對象的函數的參數。
使用引用參數的主要緣由
1須要修改調用函數中的數據對象 2提升效率
使用引用與指針的指導原則:
對於使用傳遞的值而不做修改的函數
1數據對象很小,如內置數據類型或小型結構,按值傳遞
2數據對象是數組,用指針,這是惟一選擇
3數據對象是較大的結構,用const指針或const引用
4數據對象是類對象,使用const引用(傳遞類對象參數的標準方式是按引用傳遞)
對於修改調用函數中數據的函數
1數據對象是內置數據類型,使用指針
2數據對象是數組,用指針,這是惟一選擇
3數據對象是結構,使用引用或指針
4數據對象是類對象,使用引用
對於帶參數列表的函數,必須從右向左添加默認值。即,要爲某個參數設置默認值,則必須爲它右邊的全部參數提供默認值。
函數重載的關鍵是函數的參數列表---也稱爲函數特徵標(function signature).若是兩個函數的參數數目和類型相同,同時參數的排列順序也相同,則它們的特徵標相同,而變量名是可有可無的。
編譯器在檢查函數特徵標時,將把類型引用和類型自己視爲同一特徵標
後置返回類型(trailing return type)將返回類型移到了參數聲明後面,可用於函數定義
Auto f (int x, float y) ->double {;}
Auto在這裏是一個佔位符,表示後置返回類型提供的類型
Template<class T1, class T2>
Auto f(T1 x, T2 y) ->decltype(x+y)
{
Return x+y ;
}
如今,decltype在參數聲明後面,所以x和y位於做用域內,可使用它們。
若是沒有顯示的初始化靜態變量,編譯器將把它設置爲0,靜態數組和結構將每一個元素或成員的全部位都設置爲0
代碼塊中使用static時,將致使局部變量的存儲持續性爲靜態的。這意味着該變量只在該代碼塊中可用,該代碼塊不處於活動狀態時仍然存在,兩次函數調用之間,靜態局部變量的值將保持不變,(靜態變量適用於再生—能夠用它將瑞士銀行的祕密帳號傳遞到下一個要去的地方)。另外若是初始化了靜態局部變量,則程序只在啓動時進行一次初始化,之後再調用函數時,將不會像自動變量那樣再次被初始化。
C++不容許在一個函數中定義另一個函數,所以全部的函數的存儲持續性都自動爲靜態的。可使用關鍵字static將函數的連接性設置爲內部的,使之只能在一個文件中使用,這要求必須在原型和函數定義中使用該關鍵字
Static int private(double x);
…
Static int private(double x){;}
一般,new負責在堆(heap)中找到一個足以知足要求的內存塊,new運算符還有另外一種變體,被稱爲定位(placement)new運算符,它讓您可以指定要使用的位置。程序員可使用這種特性來設置其內存管理規程,處理須要經過特定地址進行訪問的硬件或在特定位置建立對象。
要使用定位new特性,首先須要包含頭文件<new>,它提供的這種版本的new運算符的原型;而後將new運算符用於提供了所需地址的參數。除須要指定參數外,句法與常規new運算符相同。下面的代碼段演示了new運算符的4種用法:
Struct chaff
{
Char dross[20]; Int slag;
};
Char buffer1[50];
Char buffer2[500];
Int main()
{
Chaff *p1, *p2; Int *p3, *p4;
//first, the regular forms of new
P1 = new chaff; //place structure in heap P3 = new int[20]; //place tructure in heap
//now, the two forms of placement new
P2 = new(buffer1) chaff; //place structure in buffer1 P4 = new(buffer2) int[20]; //place int array in buffer2
…
}
Delete只能用於指向常規new運算符分配的堆內存的指針,若是使用new[]來分配內存,則應使用delete[]來釋放內存。而使用定位new運算符分配內存的對象,必須顯式的調用析構函數,這是須要顯式調用析構函數的少數幾種情形之一。若是有指向對象的指針,能夠這樣作:ptr-> ~Test();
Using debts::Debt; //makes the Debt structure definition available
Using debts::showDebt; //makes the showDebt function available
注意using聲明只使用了名稱,例如第二個聲明沒有描述showDebt的返回類型或特徵標,而只給出了名稱,所以,若是函數被重載,則一個using聲明將導入全部版本。
C++程序員一般使用類來實現類描述,而把結構限制爲只表示純粹的數據對象
定義位於類聲明中的函數都將自動成爲內聯函數
在設計類時,一般應提供對全部類成員作隱式初始化的默認構造函數,但只能有一個默認構造函數。默認構造函數能夠沒有任何參數,若是有,則必須給全部參數提供默認值。若是有多個構造函數,則必須以相同的方式使用new,要麼都帶[],要麼都不帶[],由於只有一個析構函數,全部的構造函數都必須與它兼容。
只接受一個參數的構造函數定義了從參數類型到類類型的轉換,若是使用關鍵字explicit限定了這種構造函數,則它只能用於顯示轉換(bean = B(10) ),不然也能夠用於隱式轉換。構造函數只用於從某種類型到類類型的轉換。要進行相反的轉換,必須使用特殊的C++運算符函數—轉換函數。而且最好使用顯示轉換而避免隱式轉換(explicit operator int()/double() const;)相關狀況下,將加法定義爲友元可讓程序更容易適應自動類型轉換,緣由在於,兩個操做數都成爲函數參數,所以與函數原型匹配(total = p+j;轉換爲total = operator+(p+j);)若是常常須要將double值與類對象相加,則重載加法更合適;若是隻是偶爾使用則依賴自動轉換更簡單,但爲了更保險,可使用顯式轉換
就像應儘量將const引用和指針用做函數形參同樣,只要類方法不修改調用對象,就應將其聲明爲const(類方法void stack::show() const)
Const Stock& Stock::topval(const Stock& s) const;該函數隱式地訪問一個對象,而顯示地訪問另外一個對象,並返回其中一個對象的引用。括號中的const代表不會修改被顯式訪問的對象,而括號後面的const代表不會修改被隱式訪問的對象,因爲返回了兩個const對象之一的引用所以返回類型也爲const引用。
初始化對象數組的方案是,首先使用默認構造函數建立數組元素,而後花括號中的構造函數將建立臨時對象,而後將臨時對象的內容複製到相應的元素中。所以要建立類對象數組,則這個類必須有默認構造函數。
Class Bakery
{
Private:
const int Months = 12; //wrong Static const int Months =12; //right
Enum{Months = 12}; //right
Double costs[Months]; …
}
類聲明只是描述了對象的形式,並無建立對象。所以在建立對象前沒有用於存儲值的空間,cosnt方法是行不通的。
在類聲明中聲明的枚舉的做用域爲整個類,所以能夠用枚舉爲整型常量提供做用域爲整個類的符號名稱。注意,用這種方式聲明枚舉不會建立類數據成員。也就是說,全部對象中都不包含枚舉。另外Months只是一個符號名稱,在做用域爲整個類的代碼中遇到它,編譯器將用12來替換它。因爲這裏使用枚舉只是爲了建立符號常量,並不打算建立枚舉類型的變量,所以不須要提供枚舉名。
可使用關鍵字static在類中定義常量,這將建立一個名爲Months的常量,該常量將與其餘靜態變量存儲在一塊兒,而不是存儲在對象中。所以只有一個Months常量,被全部Bakery對象共享。
Time Time::operator+(const Time& t) const { }
…
Total = coding + fixing;
這將調用operator+()方法。在運算符表示法中,運算符左側的對象(這裏爲coding)是調用對象,運算符右邊的對象(這裏爲fixing)是做爲參數被傳遞的對象
=,(),[],->只能經過成員函數進行重載
對於非成員重載運算符函數來講運算符表達式左邊的操做數對應於運算符函數的第一個函數,運算符表達式右邊的操做數對應於運算符函數的第二個參數。
當運算符函數是成員函數時,第一個操做數將是調用該函數的對象,
若是要爲類重載運算符,並將非類的項做爲其第一個操做數,則能夠用友元函數來反轉操做數的順序。好比:重載<<運算符,使之能夠與cout一塊兒使用,要讓ostream對象成爲第一個操做數,須要將運算符函數定義爲友元,並返回ostream&
ostream& operator<<(ostream& os, const c_name& obj)
{
os<< …; return os;
}
由於operator<<()直接訪問Time對象的私有成員,因此它必須是Time類的友元;但因爲它並不直接訪問ostream對象的私有成員,因此並不必定必須是ostream類的友元。
只有在類聲明中的原型中才能使用friend關鍵字,除非函數定義也是原型,不然不能在函數定義中使用friend關鍵字。
若是方法經過計算獲得一個新的類對象,則應考慮是否可使用類構造函數來完成這種工做,這樣不只能夠避免麻煩,並且能夠確保新的對象是按照正確的方式建立的。
產生隨機數
srand(time(0)); //seed random-number generator
D = rand()%360; //
C++自帶了一個<random>頭文件,產生隨機數的功能很強大
類.h中的static成員在類.cpp中初始化(int Class_n:: numb = 0;)注意在類聲明中不能初始化靜態成員變量,由於聲明描述瞭如何分配內存,但並不分配內存。初始化語句指出了類型,並使用了做用域運算符但沒有使用關鍵字static。有一種例外狀況:靜態數據成員爲const整數類型或枚舉型,則能夠在類聲明中初始化。
每當程序生成了對象副本時,編譯器都將使用複製構造函數,具體地說,當函數按值傳遞對象或返回對象時,都將使用複製構造函數,因爲按值傳遞對象將使用複製構造函數,所以應該按引用傳遞對象。這樣能夠節省調用構造函數的時間以及存儲新對象的空間。
默認的複製構造函數逐個複製非靜態成員,複製的是成員的值(也稱淺複製),若是成員自己也是類對象,將使用這個類的複製構造函數來複製成員對象。靜態函數不受影響,由於它們屬於整個類,而不是各個對象。
若是構造函數包含靜態數據成員,而且其值在新對象建立時發生變化,則應該提供一個顯式複製構造函數來處理計數問題,有時必須提供一個複製構造函數的緣由在於,一些類成員是使用new初始化的、指向數據的指針,而不是數據自己。這須要深度複製。相似於深度賦值1,檢查自我賦值狀況,2,釋放成員指針之前指向的內存,3,複製數據而不只僅是數據的地址,4,返回一個指向調用對象的引用,以連續賦值。
使用new的類一般須要包含顯式複製構造函數和執行深度複製的賦值運算符,是否須要顯式提供取決於默認的成員複製是否合適
一般構造函數使用new時,須要注意:析構函數、複製構造函數、重載賦值運算符
派生類使用new時,必須爲派生類定義,顯式析構函數、複製構造函數、賦值運算符
函數應當避免將對象賦給自身;不然給對象從新賦值前,釋放內存操做可能刪除對象的內容賦值運算符返回一個指向調用對象的引用,這位作能夠像常規賦值操做那樣,連續進行賦值。
在重載時,C++區分常量和很是量函數的特徵標
能夠將成員函數聲明爲靜態的(函數聲明必須包含關鍵字static,但若是函數定義是獨立的,則其中不能包含關鍵字static)。後果有兩個,1,不能經過對象調用靜態成員函數,實際上,靜態成員函數也不能使用this指針。若是靜態成員函數是在公有部聲明的,則可使用類名和做用域解析運算符來使用它。2,因爲靜態成員函數不與特定的對象相關聯,所以只能使用靜態數據成員
有關返回對象的說明
首先,返回對象將調用複製構造函數,而返回引用不會。
其次,引用指向的對象應在調用函數執行時存在,局部變量是不行的,由於已經被析構
最後,參數聲明爲const時,若返回參數引用,返回類型必須爲const
常見的返回非const對象情形是,重載賦值運算符或重載與cout一塊兒使用的<<運算符,前者旨在提升效率,然後者是隻能這樣作。
類中包含const成員或聲明爲引用的成員時,構造函數必須使用成員初始化列表語法(member initializer list),從概念上說,調用構造函數時,對象將在括號中的代碼執行以前被建立,調用構造函數將致使程序先給類成員變量分配內存,而後程序流程進入到括號中,再使用常規賦值方式將值存儲到內存中,所以,對於const數據成員,必須在執行到構造函數體以前,即建立對象時進行初始化。C++提供了一種特殊的語法完成上述工做,即成員初始化列表,由逗號分隔的初始化列表組成(前面帶冒號)它位於參數列表的右括號以後,函數體左括號以前,經過初值能夠是常量或構造函數的參數列表中的參數,這種方法並不限於初始化常量,其餘類成員亦可,但只有構造函數可使用這種初始化列表語法。
Queue::Queue(int qs) : qsize(qs) //qsize是const類型
{
front = rear = nullptr; items = 0;
}
Queue:Queue(int qs) : qsize(qs), front(nullptr), rear(nullptr), items(0)
{
}
const與引用同樣,只能在被建立時進行初始化,對於簡單的數據成員使用成員初始化列表語法與函數體中賦值沒什麼區別,但,對於自己就是類對象的成員來講,成員初始化列表的效率更高,
數據成員被初始化的順序與它們出如今類聲明中的順序相同,與初始化列表中的排列順序無關。
在類定義中初始化,等價於成員初始化列表,然而若是構造函數調用成員初始化列表,則類內初始化將被覆蓋
派生類構造函數必須使用基類構造函數,建立時派生類對象時,C++使用成員初始化列表語法首先建立基類對象,若是不調用基類構造函數,程序將使用默認的基類構造函數,派生類對象過時時,程序將首先調用派生類析構函數,而後再調用基類析構函數。
派生類對象可使用基類的方法,條件是方法不是私有的,基類指針能夠在不進行顯式類型轉換的狀況下指向派生類對象,基類引用能夠在不進行顯式類型轉換的狀況下引用派生類對象,但,基類指針或引用只能調用基類方法。
若是要在派生類中從新定義基類的方法,一般應將基類方法聲明爲虛的。這樣,程序將根據對象類型而不是引用或指針的類型來選擇方法版本。爲基類聲明一個虛析構函數也是一種慣例,(除非該類不用作基類),這樣當經過指向對象的基類指針或引用來刪除派生類對象時,程序將首先調用派生類的析構函數,而後調用基類的析構函數,而不只僅是調用基類的析構函數。
公有繼承是最經常使用的方式,它創建一種is-a關係,即派生類對象也是一個基類對象,能夠對基類對象執行的任何操做,也能夠對派生類對象執行。
私有繼承是has-a關係的一部分,得到實現,不得到接口。包含將對象做爲一個命名的成員對象添加到類中,而私有繼承將對象做爲一個未命名的繼承對象添加到類中。
保護繼承是私有繼承的變體,也是has-a關係。
包含創建的也是has-a關係,與私有繼承和保護繼承相比,包含更容易實現和使用,一般優先採用包含的方式。然而私有繼承和保護繼承比包含有一些不一樣的功能,例如,繼承容許派生類訪問基類的保護成員,還容許派生類從新定義從基類繼承的虛函數,另外一方面若是須要使用某個類的幾個對象,則用包含理適合。
多重繼承MI使得可以在類設計中重用多個類的代碼。MI會帶來一些問題,即屢次定義同一個名稱,繼承多個基類對象等。可使用類限定符來解決名稱二義性問題,使用虛基類避免繼承多個基類對象的問題,但,使用虛基類後,就須要爲編寫構造函數初始化列表以及解決二義性問題引入新規則。
從新定義繼承的方法並非重載,若是從新定義派生類中的函數,不僅是使用相同的函數參數列表覆蓋基類聲明,而是隱藏全部同名基類方法。這引出了兩條經驗規則:1,若是從新定義繼承的方法,應確保與原來的原型徹底相同,但若是返回類型是基類的引用或指針,則能夠修改成指向派生類的引用或指針,這稱爲返回類型協變(covariance of return type)2,若是基類聲明被重載了,應在派生類中從新定義全部的基類版本。若是不需修改,則新定義能夠只調用基類版本。(void h::show() const {L::show();},沒有從新定義的版本將被隱藏,派生類對象將沒法使用它們。
對於外部世界來講,保護成員的行爲與私有成員類似,但對於派生類來講,保護成員的行爲與公有成員類似。對於成員函數來講,保護訪問控制頗有用,它讓派生類可以訪問公衆不能使用的內部函數。
C++經過使用純虛函數(pure virtual function)提供未實現的函數。純虛函數聲明的結尾處爲=0,(virtual double Area() const = 0;)當類聲明中包含純虛函數時,不能建立該類的對象。這裏的理由是:包含純虛函數的類只用做基類(abstract base class, ABC),抽象基類。C++容許純虛函數有定義,能夠在基類實現文件中進行定義,而將原型聲明爲虛的(void Move(int nx, int ny) = 0,總之,在原型中使用=0指出類是一個抽象基類,在類中能夠不定義該函數。ABC描述的是至少使用一個純虛函數的接口,從ABC派生出的類將根據派生類的具體特徵使用常規虛函數來實現這種接口。
不必定非得定義純虛方法,對於包含純虛成員的類,不能使用它來建立對象,純虛方法用於定義派生類的通用接口。
當基類和派生類都採用動態內存分配時,派生類的析構函數、複製構造函數、賦值運算符都必須使用相應的基類方法秋處理基類元素,這種要求是經過三種不一樣的方式完成的,1,析構函數自動完成,2,構造函數,經過在初始化成員列表中調用基類的複製構造函數來完成,若是不這樣作將自動調用基類的默認構造函數,3,賦值運算符,經過使用做用域解析運算符顯式調用基類賦值運算符完成。
構造函數、析構函數、賦值運算符都是不能被繼承的,由於基類的構造函數、析構函數都在派生類構造和析構時使用,而賦值運算符是由於包含一個類型爲其所屬類的形參,
ostream等友元不是成員函數,因此派生類實現文件中不能使用做用域解析運算符來指出使用基類與派生類中哪一個operator<<函數,只能使用強制類型轉換,以匹配原型時能選擇正確的函數
std::ostream& operator<<(std::ostream& os, const hasDMA& hs)
{
os<<(const baseDMA&)hs; os<<」style:」<<hs,stytle; return os;
}
私有繼承提供無名稱的子對象成員,而包含提供顯式命名的對象成員。
在構造函數中包含使用成員名標識構造函數
Student(const char* str):name(str) {}
私有繼承使用類名:
Student(const char* str):std::string(str) {}
包含使用對象名調用方法,私有繼承使用類名和做用域解析運算符調用方法
在私有繼承中,未進行顯式類型轉換的派生類引用或指針,沒法賦值給基類的引用或指針。
包含可以包括多個同類的子對象,而私有繼承只能使用一個某類型的對象。
一般應使用包含來創建has-a關係;若是新類須要訪問原有類的保護成員或須要從新定義虛函數,則應使用私有繼承。
使用保護派生或私有派生時,基類的公有成員將成爲保護成員或私有成員,假設要讓基類的方法在派生類外面可用,方法之一是定義一個使用該基類方法的派生類方法;另外一種方法是,將函數調用包裝在另外一個函數調用中,即便用一個using聲明來指出派生類可使用特定的基類成員,即便採用的是私有派生。例如但願Student可使用val的方法max()
class Student :private std::string, private std::val<double>
{
…
public:
using std::val<double>::max
…
};
上述using聲明使得基類方法就像是派生類方法同樣,但只適用於繼承,不適用於包含。
cout<<」high score: 「<<ada[i].max()<<endl;
using C1::fn;
double fn(double){};
派生類C2中的using聲明讓C2對象可以使用基類C1的三個fn()方法,但將選擇C2而不是C1定義的fn(double)
在C++11中,可以使用虛說明符override指出您要覆蓋的一個虛函數:將其放在參數列表後面,若是聲明的與基類方法不匹配,編譯器將視爲錯誤
virtual void f(char* ch) const override {std::cout<<val();}
說明符final解決了另外一個問題。您可能想禁止派生類覆蓋特定的虛方法,爲此可在參數列表後面加上final
virtual void f(char* ch) const final {std::cout<<val();}
在多重繼承MI中必須使用關鍵字public限定每個基類,由於編譯器默認私有派生。C++引入多重繼承的同時,引入了一種新技術—虛基類(virtual base class),使得MI成爲可能,虛基類使得從多個類(它們的基類相同)派生出的對象只繼承一個基類對象。例如可在類聲明中使用關鍵字virtual使得Worker被用做Singer和Waiter的虛基類(virtual和public的次序可有可無)
class Singer: virtual public Worker {…};
class Waiter: public virtual Worker {…};
class SingingWaiter: public Singer, public Waiter {…};
如今SingingWaiter對象只包含Worker對象的一個副本。
虛基類和虛函數之間並不存在明顯的聯繫。
若是類有間接虛基類,則除非只需使用該虛基類的默認構造函數,不然必須顯式地調用該虛基類的某個構造函數。C++在基類是虛的時,禁止信息經過中間類自動傳遞給基類,由於多重繼承MI有多條途徑傳遞信息給虛基類,會發生衝突。
如下是禁止的:
SingingWaiter(const Worker& wk, int p=0, int v =Singer::other)
:Waiter(wk,p), Singer(wk,v) {}
如下是正確的:
SingingWaiter(const Worker& wk, int p=0, int v =Singer::other)
:Worker(wk), Waiter(wk,p), Singer(wk,v) {}
對於單繼承,若是沒有定義Show(),將使用最近祖先中的定義,而在MI中每一個直接祖先都有一個Show()函數,這會產生二義性。
解決方法一:使用模塊化方式,而非遞增方式,即提供一個只顯示Worker組件的方法和一個只顯示Waiter組件或Singer組件的方法。而後在SingingWorker::Show()方法中組合起來。
void Worker::Data() const
{
cout<<」name: 「<<fullname<<」\n」; cout<<」Employee ID: 「<<id<<」\n」;
}
void Waiter::Data() const
{
cout<<」panache rating:」<<panache<<」\n」;
}
void Singer::Data() const
{
cout<<」Vocal range:」<<pv[voice] <<」\n」;
}
void SingingWaiter::Data() const
{
Singer::Data(); Waiter::Data();
}
void SingingWaiter::Show() const
{
cout<<」Category: singing waiter\n」; Worker::Data(); Data();
}
與此類似,其餘Show()方法能夠組合適當的Data()組件。且Data()應當設置爲保護的方法,這樣便只能在繼承層次結構中的類中使用它,在其餘地方不能使用
總之在祖先相同時,使用MI必須引入虛基類,並修改構造函數初始化列表的規則,另外,若是在編寫這些類時沒有考慮到MI,則還可能須要從新編寫它們。
經過多條虛途徑和非虛途徑繼承某個特定的基類時,該類將包含一個表示全部的虛途徑的基類子對象和分別表示各條非虛途徑的多個基類子對象。
派生類中的名稱優先於直接或間接祖先類中的相同名稱。
template <class T, int n>
模板頭中的表達式參數能夠是整型、枚舉、引用或指針。(double m 是非法的,double*m是合法的),另外模板代碼不能修改參數的值,也不能使用參數的地址,因此在模板中不能使用諸如n++和&n等表達式。另外實例化模板時,用途表達式參數的值必須是常量表達式。
template <class T1, class T2 =int>
class Topo{…};
能夠爲類模板類型參數提供默認值,但不能爲函數模板參數提供默認值。然而,能夠爲非類型參數提供默認值。
TP<double, 30> *pt;建立指針
pt = new TP<double, 30>建立對象
編譯器在須要對象以前,不會生成類的隱式實例化,類定義(實例化)在聲明類對象並指定特定類型時生成
當使用關鍵字template並指出所需類型來聲明類時,編譯器將生成類聲明的顯式實例化(explicit instantiation)例以下面的聲明將TP<string, 100>聲明爲一個類:
template class TP<string, 100>;
顯式具體化是特定類型的定義,要提供一個專供const char*使用的Sorted模板,示例以下:
template <> class Sorted<const char*>{…};
C++容許部分具體化,即給類型參數之一指定具體的類型。
template <class T1, class T2> class Pair{};
template<class T1> class Pair<T1, int>{};
template後面的<>聲明的是沒有被具體化的類型參數,所以第二個聲明將T2具體化爲int但T1不變,注意,若是指定全部的類型,則<>內將爲空,這將致使顯式具體化。
模板類的約束模板友元函數
首先,在類定義前面聲明每一個模板函數
template <class T> void counts();
template <class T> void report(T&);
而後,在函數中再次將模板聲明爲友元,這些語句根據類型參數的類型聲明具體化。
template <class TT>
class HasT
{
friend void counts<TT>(); friend void report<>(HasT<TT>&);
};
聲明中<>指出這是模板具體化,對於report(),<>能夠爲空,由於能夠從函數參數推斷出以下模板類型參數:HasT<TT>
但counts()函數沒有參數,所以必須使用模板參數語法<TT>指明其具體化。還須要注意的是TT是HasT類的參數類型。
最後,爲友元提供模板定義。
模板類的非約束模板友元函數
經過在類內部聲明模板,能夠建立非約束友元函數,即每一個函數具體化都是每一個類具體化的友元,對於非約束友元,友元模板類型參數與模板類類型參數是不一樣的。
template <class T>
class Many
{
template <class C, class d> friend void show(C&, D&);
};
可使用using爲模板具體化指定別名
template<class T>
using arrtype = std::array<T, 12>;
這將arrtype定義爲一個模板別名,可以使用它來指定類型
arrtype<double> gallons;
arrtype<int> days;
arrtype<std::string> months;
C++容許將語法using =用於非模板。與常規typedef等價,介可讀性更強
typedef const char* pc1;
using pc2 = const char*
typedef const int(pa1)[10];
using pa2 = const int()[10];
友元類的友元聲明能夠位於類的公有、私有或保護部分,其全部的位置可有可無。
friend class Remote;
友元成員函數則必須當心排列各類聲明和定義的順序。
class Tv; //forward declaration
class Remote{}; //Tv-using methods as prototypes only
class Tv{};
//put Remote method definitions here
typeid運算符使得可以肯定兩個對象是否爲同種類型。它與sizeof有些相像,能夠接受兩種參數:類名,結果爲對象的表達式
typeid(Magnificent) == typeid(*pg)
cout<<」now processing type」<<typeid(*pg).name();
若是在擴展的if else語句系列中使用了typeid則應考慮是否應該使用虛函數和dynamic_cast
dynamic_cast使得可以在類層次結構中進行向上轉換,而不容許其餘轉換。
High bar;
const High* pbar = &bar;
High pb = const_cast<High>(pbar);
*pb成爲一個可用於修改bar對象值的指針,它刪除了const標籤。提供該運算符的緣由是,有時候可能須要這樣一個值,它在大多數時候是常量,而有時又是能夠修改的。在這種狀況下,能夠將該值聲明爲const並在須要的時候使得const_cast
static_cast<type-name>(expression)
僅當type-name可被隱式轉換爲expression所屬的類型或expression可被隱式轉換爲type-name所屬的類型時,上述轉換才合法
嵌套類是在其餘類中聲明的類,它有助於設計這樣的助手類,即實現其餘類,但沒必要是公有接口的組成部分。
有兩種訪問權限適合於嵌套類,1,嵌套類的聲明位置決定了嵌套類的做用域,即它決定了程序的哪些部分能夠建立這種類的對象,2,和其餘類同樣,嵌套類的公有部分、保護部分、私有部分控制了對類成員的訪問,在哪些地方可使用嵌套類以及如何使用嵌套類,取決於做用域和訪問控制。
程序試圖將一個unique_ptr賦給另外一個時,若是源unique_ptr是個臨時右值,編譯器容許這樣作;若是源unique_ptr將存在一段時間,編譯器將禁止這樣作
若是程序要使用多個指向同一個對象的指針,應選擇shared_ptr這樣的狀況包括:1,有一個指針數組,並使用一些輔助指針來標識特定的元素,如最大最小元素。2,兩個對象都包含第三個對象的指針;3,STL容器包含指針。
若是程序不須要多個指向同一個對象的指針,可以使用unique_ptr.若是函數使用new分配內存,並返回指向該內存的指針,將其返回類型聲明爲unique_ptr是不錯的選擇。這樣,全部權將轉讓給接受返回值的unique_ptr,可將unique_ptr存儲到STL容器中,只要不調用將一個unique_ptr複製或賦給另外一個的方法或算法(如sort())
全部STL容器都提供了一些基本方法,
size() 返回容器中元素數目
swap() 交換兩個容器內容
begin() 返回一個指向容器中第一個元素的迭代器
end() 返回一個表示超過容器尾的迭代器
迭代器的行爲就像指針。模板使得算法獨立於存儲的數據類型,而迭代器使算法獨立於使用的容器類型,還有一個C++自動類型推斷頗有用的地方
vector<double>::iterator pd = scores.begin();
改成 auto pd = scores.begin();
erase(it1, it2) 刪除給定區間元素
insert(it1, it2, it3) 插入位置、插入起止區間
vector的成員函數swap()效率比非成員的高,但非成員的可以交換兩個類型不一樣的容器的內容。
for_each(it1, it2, f );將被指向的函數應用於區間各元素,但不能修改元素值,適合全部容器
基於範圍的for循環是爲用於STL設計的
for(auto x: books) show(x);
for(auto& x:books) chage(x);
Random_shuffle(it1, it2) 隨機排列區間元素,要求容器類容許隨機訪問
copy(it1, it2, it3)複製區間,第一個元素複製到的位置
爲區分++運算符的前綴後綴版本,C++將operator++做爲前綴版本,將operator++(int)做爲後綴版本,其中的參數永遠也不會被用到,因此沒必要指定其名稱。
做爲一種編程風格,最好避免直接使用迭代器,而應儘量使用STL函數(如for_each())來處理細節
ostream_iterator<int,char> out_iter(cout,」 」);
copy(v.begin(), v,end(), out_iter);
out_iter迭代器是一個接口,讓您可以使用cout來顯示信息,4個參數分別表示,數據類型,字符類型,要使用的輸出流,每一個數據項後顯示的分隔符。也能夠直接使用匿名迭代器
copy(v.begin(), v,end(), ostream_iterator<int,char> (cout,」 」));
使用反向迭代器反向顯示內容
copy(v.rbegin(), v,rend(), out_iter);
若是能夠在顯式聲明迭代器和使用STL函數來處理內部問題,之間選擇,請採用後者,後一種方法作的工做少,人爲出錯機會少。
out_iter能夠換成其餘迭代器以下,複製算法就變爲插入算法
back_insert_iterator
front_insert_iterator
insert_iterator//將元素插入到構造函數參數指定的位置前面。
聲明方法爲:insert_iterator<vector<int>> insert_iter(v, v,begin());
容器種類:
deque若是多數操做發生在序列的起始和結尾處,應考慮使用
list
queue
priority_queue
stack
vector是最簡單的序列類型,除非其餘類型特殊優勢更好知足要求,不然應使用這種
map
multimap
set
multiset
bitset
forward_list
unordered_map
unordered_multimap
unordered_set
unordered_multiset
其中bitset不視爲容器,視爲一種獨立的類別
關聯容器的優勢在於,提供了對元素的快速訪問,與序列類似,關聯容器也容許插入新元素,但不能指定元素的插入位置,緣由是關聯容器一般有用於肯定數據放置位置的算法,以便可以快速檢索信息,(一般是使用某種樹實現的)
無序關聯容器是基於數據結構哈希表的,旨在提升添加和刪除元素的速度以及提升查找算法的效率
不少STL算法都使用函數對象--也叫函數符(functor)函數符是能夠以函數方式與()結合使用的任意對象,包括函數名、指向函數的指針和重載了()運算符的類對象
STL是一個容器類模板、迭代器類模板、函數對象模板、和算法函數模板的集合,它們的設計是一致的,都是基於泛型編程原則的。算法經過使用模板,從而獨立於所存儲的對象的類型,經過使用迭代器接口,從而獨立於容器的類型,迭代器是廣義指針。
STL算法可用於非STL容器,如常規數組、string對象、array對象以及您設計的秉承STL迭代器和容器規則的任何類
模板類complex和valarray支持複數和數組的數值運算。
一般使用緩衝區能夠更高效地處理輸入和輸出,一般緩衝區爲512字節或其整數倍,當標準輸出鏈接的是硬盤上的文件時,緩衝能夠節省大量的時間。鍵盤輸入每次提供一個字符,所以在這種狀況下,程序無需緩衝區來幫助匹配不一樣的數據傳輸速率,然而,對鍵盤輸入進行緩衝可讓用戶在將輸入傳輸給程序以前返回並更正。
多數C++實現都會在輸入即將發生時刷新緩衝區
控制符flush刷新緩衝區,而控制符endl刷新緩衝區並插入一個換行符
關閉文件將刷新緩衝區,從而確保文件被更新。
若是須要同時打開兩個文件,則必須爲每一個文件建立一個流。然而,若是要依次處理一組文件,例如可能要計算某個名稱在10個文件中的出現次數,則能夠打開一個流,並將它依次關聯到各個文件,這在節省計算機資源方面比每一個文件打開一個流的效率高。
程序讀取並顯示整個文件後,將設置eofbit元素,這使程序相信,它已經處理完文件,並禁止對文件作進一步的讀寫,使用clear()方法重置流狀態,並打開eofbit後,程序即可以再次訪問該文件。
對於其餘類型的指針,C++將其對應於void,並打印地址的數值表示,若是要得到字符串的地址,則必須將其強制轉換爲其餘類型如(cout<<(void) amout;
C++11新增了右值引用,這是使用&&表示的,右值引用可關聯到右值,便可出如今賦值表達式右邊,但不能對其應用地址運算符的值,右值包括字面常量(C字符串除外,它表示地址)、諸如x+y等表達式、返回值的函數(條件是該函數返回的不是引用)
int x = 10;
int y = 23;
int && r1 =13;
int && r2 = x+y;
double &&r3 = std::sqrt(2.0);
注意,r2關聯到的是當時計算x+y獲得的結果。也就是說,r2關聯到的是33,即便之後修改了x或y,也不會影響到r2
將右值關聯到右值引用致使該右值被存儲到特定的位置,且能夠獲取該位置的地址。
引入右值引用的主要目的之一是實現移動語義。
在將全部權轉移給新對象的過程當中,移動構造函數可能修改其實參,這意味着右值引用參數不該是const,,移動賦值運算符也同樣
lambda函數
[](int x) {return x&3 ==0;}
返回類型至關於使用decltyp根據返回值推斷獲得的,這裏爲bool,若是不包含返回語句,推斷出的返回類型將爲void
僅當lambda表達式徹底由一條返回語句組成時,自動類型推斷才管用,不然,須要使用新增的返回類型後置語法
[](double x)->double{int y=x; return x-y;}
可給lambda指定一個名稱,像使用函數那樣使用有名稱的lambda
auto mod = [](int x){return x%3 == 0;}
bool result = mod(z) //result is true if z%3==0
最後lambda有一些額外的功能。具體地說,lambda可訪問做用域內的任何動態變量;要捕獲使用的變量,可將其名稱放在[]內。
若是隻指定了變量名,如[z]將按值訪問變量;
若是在名稱前加上&如[&count]將按引用訪問變量。
[&]可以按引用訪問全部動態變量。
[=]可以按值訪問全部動態變量。
[ted, &ed]可以按值訪問ted以及按引用訪問ed,
[&, ted]可以按值訪問ted以及按引用訪問其餘全部動態變量,
[=, &ed]可以按引用訪問ed以及按值訪問其餘全部動態變量
C++引入lambda的主要目的是將相似於函數的表達式用做接受函數指針或函數符的函數的參數。所以,典型的lambda是測試表達式或比較表達式,可編寫爲一條返回語句,這使得lambda簡潔易懂,且可自動推斷返回類型。