程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++學習筆記

C++學習筆記

編輯:C++入門知識

C++學習筆記


C++ Primer Plus中文版第5版(632)


1.char 作為數值類型,則unsigned char表示的數據范圍為0~255 而signed char范圍為-128~127。


2.cin.get(name,size);是不接收換行符,把它留在了輸入流中。cin.get()只接收一個字符,包括換行符。(getline()用法類似)
當get()讀取空行後將設置失效位,接下來的輸入將被阻斷,可用cin.clear()來恢復輸入。


3.枚舉只定義了賦值操作符(在體外只能把enum值賦給enum變量),沒有定義算術運算。


4.靜態,有鏈接性:全局變量(分內外鏈接性,static為內部鏈接)。當file1.cpp中定義了全局變量 int tom;和static int m=0;而在file2.cpp中想用file1.cpp中的
tom的數據,同時想在file2.cpp中定義自己的m.file2.cpp中可以這樣聲明:extern int tom; int m;想要在當前文本中使用另一個
文本的同名變量數據,則必須編譯兩個文本產生後綴為 .o 的文件和在當前文本中用extern聲明想用的變量(一般是不可以初始化,但extern cost 聲名的變量可以初始化),這樣才能成功鏈接使用。


5.用mutable聲明的變量m,即使是const變量也可以修改該變量,如:結構體中定義了nutable變量,再定義一個const的結構體變量node,則node.m是可以被重新修改的。


6.外部定義靜態屬性的const或static變量的鏈接性為 內部的(意為在所有鏈接起的文件都可以使用相同的外部聲明,屬文件私有)。如果希望內部鏈接性為外部的,則
可以使用extern關鍵字來覆蓋默認的內部鏈接性。


7.函數也有鏈接性,默認性況下函數的鏈接性為外部的。可以用關鍵字static將函數的鏈接性設置為內部的(僅在本文件中可見),使之只能在一個文件中使用。必須同時在原型聲明和函數定義中使用該關鍵字。


8.C++有一個“單定義規則”,即對於每個非內聯函數,程序中只能包含一個定義。對於鏈接性為外部的函數來說,這意味著在多文件程序中,只能有一個文件
包含該函數的定義,但使用該函的每個文件都應包含其函數原型。 內聯函數不受這項規則的約束,這允許程序員能夠將內聯函數的定義放在頭文件中。
這樣,包含了頭文件的每個文件都有內聯函數的定義。不過,C++要求同一個函數的所有內聯定義都必須相同。


9.在C++程序的某個文件中調用一個函數,如果該文件中的函數原型指出該函數是靜態的,則只在該文件中查找函數定義。若非靜態的,將在所有的程序文
件中查找,如果找到有兩個定義,編譯器將發出錯誤消息;如在程序文件中沒找到,則將在庫中搜索。


10.布局new 的一種使用:將己分配的內存再分給其他變量。
#include//使的頭文件
struct node{int a;char b[10];};
node *Node;
int *p;
char buffer[100];
Node=new (buffer)node[2];//將buffer中分配靜態空間給結構node
p=new (buffer)int[20];//將buffer中分配靜態空間給一個包含20個元素的int數組;
/*上述兩個new操作,(1)分配buffer的內存都是從相同的起始地址開始分配,要想Node與p不操作同一內存,
可以這樣:p=new (buffer+2*sizeof(node))int[20]; (2)buffer是靜態內存,所以不能用delete釋放p和Node,
要想釋放,則buffer必須為new一般分配的動態內存。*/


11.名稱空間可以是全局的,也可以位於另一個名稱空間中,但不能位於代碼塊中。所以默認情況下,在名稱空間中聲明的名稱的鏈接性為外部的(除非它引用了常量)。


12.名稱空間定義完後,可以再次添加變量名或定義,如想在己存在的名稱空間jack中再添加變量age:namespace jack{int age;}
(注:先用編譯指令using namespace jack;後向名稱空間jack添加age,那麼age也可以直接使用age變量,而不需聲明。)
當要用名稱空間jack中的age時,可以jack::age=19,或using jack::age; age=19(將age變量添加到它所屬聲明區域中)。
當用using編譯指令:using namespace 名稱空間名。這一次性聲明,但有一缺點:一個程序中使用多個名稱空間,這些名稱空間中
有相同的變量名,那麼會隱藏之前出現相同的名稱。可通過作用域分明。 用using聲明導入變量時,只能存在一個同名變量。


13.(1)名稱空間聲明進行嵌套:一個名稱空間聲明element中可以包含另一個名稱空間的聲明fire。
namespace elements
{ namespace fire
{ int flame;....}
float water;
}
using編譯指令使用內部的名稱用:suing namespace elements::fire;
(2)名稱空間中使用using 編譯指令和suing 聲明,如下所示:
namespace myth
{
using jake::age;
using namespace elements;//using 編譯指令,myth中就有了elements中的名稱
using std::cout; using std::cin;
}
假設要訪問jake::age。由於jake::age現在位於名稱空間myth中,因此可以這樣訪問:myth::age=20則jake::age也是等於20
(3)using 編譯指令具有傳遞性,如:using namespace myth;後using namespace elements也編譯了。
(4)可以給名稱空間創建別名:namespace myth_other_name=myth;
(5)未命名的名稱空間只能在所屬文件中使用,潛在作用域為:從聲明點到該聲明區域末尾(空間中的名稱相當內部鏈接性變量)。
因沒有名稱,所以不能顯示地using 編譯指令和suing聲明。


14.解釋為什新頭文件iostream要與名稱空間std同時使用:因為新頭文件iostream中使用了std名稱空間定義,#include只是將std名稱空間導入到當前文件中。
而舊頭文件iostream.h中沒有使用名稱空間定義。


15.類聲明中默認訪問控制權限為private. 類的構造函數中的參數名不能與成員變量名相同,為避免這種混亂,通常在成員變名中使用m_前綴。


16.為防頭文件被多次包含,頭文件中可以使用:#ifndef MYFILE_H_
#define MYFILE_H_
..........
#endif
17.(1)創建類對象另一種方法(初始化):
Student s=Student("name",20);這樣調用構造函數可能創建一個臨時對象(也可能不會),然後將該臨時對象復到s對象中,後該臨時對象
調用析構函數,釋放臨時內存。(可能立刻冊除,也可能會等一段時間)。
(2)賦值:s=Student("name",20); 賦值語句中使用構造函數總會導致在賦值前創建一個臨時對象。
(3)注意:如果既可通過初始化,也可通過賦值來設置對象的值,應釆用初始化方式。通常這種方式的效率更高。


18.類的const對象:調用函數時只能調用常成員函數,不能調用非 常成員函數。(原因:常對象不能被修改,而非常成員函數可能會修改對象的值,所以不能被調用)


19.接受一個參數的構造函數允許使用賦值句法來將對象初始化一個值:Classname object=value; <==> Classname object(value);


20.用new創建的對象,必須用delete來析構,這樣才會調用類中的析構函數(不是用new創建的對像自動調用析構函數)。一個類只能有一個析構函數,不帶任何參數。


21.警告:要創建類對象數組,則這個類必須有默認構造函數,如果沒有則會出錯。


22.在類中定義一個常量的三種方法:(1)在類中聲明枚舉enum (2)使用static定義常量(變量的值不能變須再加一個const修飾:static const int CONST;),類外初始化:static不必寫出。
(3)用非靜態的const聲明成員變,但這樣必須包含構造函數(要調用默認的則必須有默認構造函數,其他要調用的構造函數同理),
且每個構造函數都必須初始化用const聲明的成員變量,而且必須是這樣的形式:Classname(形參表):CONST(初始化值){....}


23.隨機函數:按一種算法用於一個初始種子值業獲得隨機數。
(1)包含在頭文件cstdlib(以前為stdlib.h) (2)函數srand(seed)是設種子用的;函數rand()取隨機數。如果自己每次取數不設種子,那麼每取出的數將作為下次取數的種子。
seed可以設置成time(0),這樣種子seed就可以隨時不同,那麼取隨機數也就不同,time()函數包含在頭文件time.h中。


24.關鍵字explicit,用來關閉對象自動轉換特性。當類中有一個參數的構造函數時:explicit Students(string name){.....}//這樣在創建對Students對象s後,不能:s=name;
但如果沒有explicit關鍵字時,可行:s=name。強制轉換是可以的,如:s=(Students)name;一般而言:只有一個這樣的成員變量才定義這樣的構造函數。


25.創建轉換函數:例如:可將Students類對象轉成string類對象的Students類成員函數:operator string(){... }//假如將double類型轉成int型通過這類型的方法,是會四捨五入取值
要求:(1)轉換函數必須是類方法 (2)轉換函數不能指定返回類型 (3)轉換函數不能有參數
警告:應小心使用隱式轉換函數。通常,最好先擇僅在被顯式地調用時才會執行的函數。


26.注意:如果一個類中重載了+運算符,第二個是double類型,並在類中又定義了operator double()成員函數,將造成混亂,因為在該類對象加上double類型值時,就有兩種
解釋,一種是調用重載+運算符執行該類加法,而另一種是將該類對象轉換成double類型,再執行double加法。


27.在執行main()之前調用其他函數做法:因為全局變量是在編譯時就創建了,所以可以定義一個全局的類對象,並在類的構造函數中調用一些想在main()之前調用的函數。


28.如果類中有引用變量,則必須包含可調用的構造函數,必須初始化引用成員變量(可對自身進行引用),而且初始化時必須在構造函數打冒號後初始化:Students(int ok):yingyou(ok){...}//yingyou為類的引用成員變量。


29.除虛基類外,類在初始化時,只能將值傳遞給直接基類。


30.記住:如果要在派生類中重新定義基類的方法,通常應將基類方法聲明為虛擬的,這樣,程序將根據所指的對象類型而不是引用或指針的類型來選擇方法版本 為基類聲明一個虛擬析構函數也是一種慣例。


31.用類名調用類的成員情況有二:(1)不須要在派生類中就可以用類名直接調用靜態成員變量 (2)要用類名調用成員函數,只有在派生類成員函數中,父類可以用自己的類名調用自身的成員函數。


32.為何需要 虛析構函數:當一個基類對象指針指向了子類對象,而這時子類對象新增的成員變量是用new分配的內存,但又想通過析構基類對象同時來析構子類新增的成員變量,那麼基類的虛擬類型將發
揮作用,動態聯編性,調用析構函數會從該基類指針對象所指的子類析構函數開始調用直至該基類,這與一般的虛函數用法一樣。如果不是虛析構函數,那麼只調用該基類的析構函數。


33.虛函數的工作原理:通常,編譯器處理虛函數的方法是:給每個類對象添加保存了一個隱藏成員表,即指向函數地址的數組指針。這種數組稱為虛函數表,它存儲了類對象進行聲明的虛函數的地址。
每個類對象都有自己的虛函數表,如果在派生類中有新定義的虛函數,則在創建該對象時也將添加到虛函數表中。
調用虛函數時:程序將查看存儲在對象中的vtbl地址,然後轉向相應的函數地址表。如果使用類聲明中定義的第三個虛函數,則程序將使用該數組中的第三個函數地址,並執行該地址的函數。
總之,使用虛函數時,在內存和執行速度方面有一定的成本,包括:
(1)每個對象都將增大,增大量為存儲地址的空間。(2)對每個類,編譯器都創建一個虛函數地址表(數組)。(3)每次虛函數調用都需要執行一步額外的操作,查地址,有時間開銷。
比較:雖然非虛函數的效率比虛函數稍高,但不具備動態聯編功能。


34.注意事項:(1)基類方法中使用關鍵字virtual 可使該方法在基類以及所有派生類中是虛擬的。
(2)如果使用指向對象的引用或指針來調用虛方法,具有動態聯編性,而不是對象的引用或指針來調用虛方法,則沒有動態聯編性。
(3)如果定義的類將被用作基類,則應將那些要在派生類中重新定義的類方法聲明為虛函數。


35.更多注意:(4)構造函數不能是虛函數 (5)析構函數應當是虛函數,除非類不用做基類。(6)友元函數不能是虛函數,因為友元函數不是類成員
( 7 )經驗規則:第一,如果重新定義繼承的方法,應確保與原來的原型完全相同,但如果返回類型是基類引用或指針,則可以修改為指向派生
類的引用或指針,這種特性被稱為返加回型協變,因為允許返回類型隨類 類型的變化而變化。
第二,如果基類聲明的函數被重載了,則應在派生類中重新定義。如不重載,則派生類對象只能調用最近一次被重載的函數。


36.(1)需要實例化的類,則類中的虛函數必須有定義。而如果該類不實例化,僅僅聲明虛函數的類且虛函數沒有實現,該類編譯是可以通過的!


(2)純虛函數出現在接口類中,並賦值為0,不要為該函數分配函數地址,從而阻止類的實例化!純虛函數是沒有定義的,如果實現了也不是純虛函數啦!


(3)一般的成員函數可以只有聲明,前提是在應用中不能調用該函數,否則會因找不到定義產生連接錯誤!


37.實例化類的虛函數必須有定義,原因如下:有虛函數作為成員函數的類, 它的實例化---對象, 在運行過程分配到的內存不止是它的成員數據, 還有一個指向該類虛函數表(vtable)的指針, 虛函
數表中的每個數據項都是一個虛函數的入口地址; 如果一個對象的虛函數只有聲明而沒有實現, 就會導致這個虛函數表找不到本應作為其數據項之一的虛函數的入口地址, 虛函數表在運行前不能裝載完成, 所以產生連接錯誤!


38.派生類使用基類的友元函數方法:
例如:基類和派生類都重載了輸出運算符,現在使用派生類的對象調用基類的友元輸出運算符:
基類友元函數:friend std::ostream & operator<<(std::ostream &os,const BaseClass bc)
{ os< 子類友元函數調用基類友元函數:friend std::ostream &operator<<(std::ostream &os,const ChildClass cc)
{ os<<(const BaseClass &)cc; os<<新增成員變量; return os;}


39.復制構造函數:類中定義了構造函數,這個構造函數中只有一個參數。而只有在創建對象時才能用=來復值指定的成員變量。
如:BaseClass(Class &k){kk=k;} Class k; BaseClass aa=k;//隱式轉換
但如果在構造函數前加關鍵字:explicit,則不能進行隱式轉換,可以顯示轉換。


40.再次強調:(1)在編寫函數的參數時,應按引用而不是按值來傳遞對象的方式,這樣做可以提高效率。因為按值傳遞時需要調用復制構造函數,然後調用析構函數,
這些調用需要時間。如不修改對象,應將參數聲明為const引用。
(2)函數返回對象和返回引用對象:相同點:都生成臨時拷貝。 不同:返回引用與按引用傳遞對象相似,在調用和被調用函數對同一個對象進行操作。
注意:函數不能返回在函數中創建的臨時對象的引用,因為當函數結束時,臨時對象將消失,因此這種引用將是非法的。


41.const函數(將const放在函數形參表後面),只是不能修改調用該函數的對象(*this). 當函數引用(或指針)返回對象時,函數返回類型必須兼容對象類型。如果對象是const類型,則函數反回類型也必須是const.


42.編譯器生成的成員函數:默認構造函數,復制構造函數,賦值操作符。


43.公有繼承的考慮因素:(1)所屬關系 (2)構造函數,析構函數,賦值操作符函數與友元函數是不能被繼承。


44.警告:如果類有間接虛基類,則除非只需使用該虛基類的默認構造函數,否則必須顯式地調用該虛基類的某個構造函數。


45.調用構造函數的順序:(1)一個派生類沒有直接虛基類:A:先調用基類的構造函數再子類;B:調用基類時,按照派生類聲明繼承基類的順序調用基類的構造函數。
(2)一個派生類有直接虛基類:A:先調用基類的構造函數再子類;B:調用基類時:b1.先調用虛基類後非虛基類,b2.再按聲明的順序被調用。


46.模板:部分具體化:即部分限制模板的通用性。例如:template class Pair {......}; template class Pair{......};


47.template
class beta{
public:
template
U blab(U u,T t);
};
template
template
U beta::blab(U u,T t){ return u+t;}


48.template