1、頭文件:標准的頭文件不帶 .h 。如果想用C語言的函數,可以用C語言的頭文件,推薦的寫法是去掉後面 ". h ",前面加上C,比如:
stdio.h ==> cstdio , math ==> cmath。
2、名字空間:標准庫中的所有名字都放在一個名叫 std 的名字空間中,使用時要加上 std:: 前綴。偷懶的辦法是用一句
usigned namespace std來表示自動在標准庫的名字前加 std 。
3、輸入輸出:cin>> 變量 /cout<< 數據,可以連成長串。
4、擴展名: .cpp , .C , .cc , .cxx
編譯連接: g++, 用法跟gcc一樣 ,或者用gcc -lstdc++ 。
注釋: /* */ , // 。
5、名字空間用法:解決名字沖突問題
定義:
namespace 名字{
變量聲明、變量定義、函數聲明、函數定義 . . .
}
多個名字的名字空間會合並在一起。如果需要也可以在名字空間裡嵌套名字空間
namespace hanbo {
namespace a{
char x;
}
double z;
}
使用: 名字空間: :內部的名字 例如:hanbo: :a: :x
統一指定名字空間
using namespace hanbo ;
c : : x
z
using namespace hanbo;
using namespace uc;
6、匿名名字空間(全局名字空間)
全局定義的 int var ; void func( );
可以直接使用名字,也可以加 : :
var = 123;
: : var = 123;
7、輸入輸出:在寫程序時不用再為類型費心了。
注意:寫程序時不用費心。運行時還是要正確輸入,如果輸入無效不會被取走而且讀取失敗。
8、在C中 結構、聯合、枚舉
定義完畢後得到的是自定義類型。
struct Student { . . . };
struct Student furong ;
在C++中使用結構、聯合、枚舉類型來定義變量時,不用再重復 struct 、 union 、enum 關鍵字。
Student furong;
結構體定義時,成員不僅可以是數據,還可以是函數,甚至可以是類型。
支持匿名聯合。
union {
int x;
char y[ 4 ];
};
枚舉不在等同於整數類型,如果需要轉換可以用強制類型轉換。
9、布爾類型:bool 在C99支持
表示是否,只有true/false兩個值,可以自動轉成整數1/0.其他類型數據當成布爾類型時非零即真零則假。
10、字符字面量:
' * '
在C語言中沒有真正的字符字面量,用的實際都是int。sizeof( ' X ' ) == 4;
在C++裡sizeof ( ' X ' ) == 1 。
11、void * 類型:
嚴格限制不能賦值給其他地址類型,
C++是一種強類型語言。
12、類型轉換:
強制類型轉換在C的基礎上增加了一種形式:類型(數據);
C++中不提倡用強制類型轉換,如果確實需要類型轉換,C++提供了另外4種方式:
satic_cast <類型> (數據)用於數字類型之間以及void * 和別的 * 類型之間
reinterpre_cast<類型> (數據)用於數值類型與地址類型之間或者地址類型相互之間。
const_cast (T 常量的地址)去掉對地址所指向的目標的const限制。
dynamic_cast 以後再說:
例子:
int a = static_cast(123.45);
int b = 100;
void *p = &b;
int *q = static_cast( p );
char *r = reinterpret_cast( q );
const int c = b;
*const_cast( &c ) = 200;
13、函數:
潛規則:1、默認返回int類型,作廢。2、空參數表表示參數的個數隨意,C++中則表示無參。因此,函數調用前必須要聲明或者定義。
重載:允許多個函數同名,但要求不同的參數表(不同類型、或者個數、或者順序,必須足以讓編譯器分清該調用哪一個函數)
編譯器無法區分該調用哪一個時則產生編譯錯誤。自動類型提升依然適用。與返回類型無關。
形式參數默認值:如果函數某個形式參數在調用時絕大多數情況下都傳遞的是某個特定實際參數,那麼就可以把這個實參值指定為
這個形參的默認值,調用時可以不再傳遞。有默認值的形參必須靠右。聲明和定義分開時,形參默認值放在聲明中。
啞元:多余的形參,不需要指定名字,一般是為了跟以前的版本兼容,偶爾也用於其他用途。
宏函數:基本不用,一是不符合強類要求,二是副作用。取而代之是內聯函數,用inline表示。內聯函數會在調用的地方展開函數代碼
而不是產生調用,跟宏函數一樣高效,但卻有明確的參數類型而且沒有副作用。是否真正執行inline的要求,完全由編譯器
自己決定。
動態內存管理:C語言用malloc、calloc、realloc、free,返回void * 。
C++用: new 類型
new 類型 (初始值)
new 類型 [ 元素個數 ]
delete 地址
delete[ ] 地址
new (指定地址)類型:在指定的地方分配內存(少用)
指定初始值直接用一對空圓括號的稱為零初始化。
char *p = new char ( ); *p == '\0';
14、new失敗會throw拋出異常導致程序終止,可以用nothrow來在申請內存失敗時像C語言的內存管理函數一樣返回NULL,頭文件。
15、古怪的關鍵字,用來表示運算符:and(&&)、 or(||)、 not(!)、 and_eq(&=)、 or_eq(|=)、 not_eq(!=)、 bitand(&)、
bitor(|)、 xor(^)、 compl(~)、xor_eq(^=)。
古怪的指針:成員指針
struct Data{
int year;
int month;
int day;
};
int (Data: : *) p = &Data: :year ;
p = &Data: : month ;
p = &Data: : day ;
16、命令提示符修改:
打開主目錄下的.bashrc文件vi ~/.bashrc
在末尾增加兩行:
PS1='\W$ '
PATH=$PATH : .
17、引用:本質上是指針(地址),編譯器自動取地址、加星號。
定義:類型 & 名字 = 初始值 ;引用必須初始化,用誰初始化它,它就跟誰是同一體,這種關系從開始到結束永不變。
特別說明:形參是在調用時由實參初始化。返回值是在返回時return後面的數據初始化。
引用使用變量本身(同一體)而不是復制數據,改變引用變量的值也就改變了它的同一體的值。
特別說明:引用不是新的數據類型而是表示傳遞方式。
18、字符串:C風格字符串數組長問題,忘記末尾的 ' \0 ' 出亂碼。
C++字符串string類型,頭文件,同樣支持像字符數組那樣按下標訪問元素,支持 +、=、各種比較運算,不用考慮
空間問題,成員函數size( )、length( )都可以取得字符串長度,成員函數c_str()可以返回對應的C風格字符串(只讀)。
19、數組:C風格的數組長度一旦確定就不能變了,作為參數傳遞就蛻變成地址無法知道長度只能再追加一個數來表示長度。
C++風格數組用vector<類型> 數組名(長度,元素初始值),用法跟數組一樣,隨時可以用成員函數resize(長度)來調整大小,
隨時可以用成員函數size( )來取得元素個數。頭文件
20、頭文件是C語言字符串處理函數的頭文件,在C++可以照樣使用,也可以換成,效果相同,唯一的區別是後者把前者裡面的內容都放到了namespace std裡。頭文件是C++語言自己的字符串類型頭文件,裡面是對string類型的定義。
21、C語言指針:指針是用來保存某種類型的變量的地址的變量,取地址&:&變量,取得某個地址的變量:* 地址,(*地址). 成員,地址 -> 成員, 向函數傳遞參數的時候,如果希望函數能操作某個變量裡的數據,就把變量的地址傳遞給函數,函數裡就可以根據地址找到內存中的那個變量,從而取得或者改變變量裡的值。地址運算:p+n,p-n,p[ i ]==>*(p+i),*p,p-q,結果是多少個單位(元素),單位為sizeof(地址類型),比較,指針定義時建議帶初始化從而避免野指針,不要通過空指針或者目標已經釋放掉的指針去找變量,不要返回非靜態局部變量的地址 作為函數的返回值,作為函數形參的指針如果不用於改變目標變量的值就盡量加const修飾保護目標不被誤傷。
22、輸出地址:輸出字符類型的地址,為了保持與C語言的兼容,系統處理成輸出字符串。如果需要輸出地址應該轉換成其它地址類型。
引用:引用一個常量,應該加const。用臨時結果(運算結果或者函數的普通返回值)去初始化一個引用時,也應該對引用加const。
const int x = 20;
const int &rx = x;
23、面向對象:Obiected-Oriented Programming
C語言是一系列函數描述程序的工作過程,稱為面向過程。C++把程序裡面的數據和對數據的操作合起來看做一個實體(稱為對象),編程工作就是操作這些對象相互作用來完成程序的功能。這樣的編程方式稱為面向對象編程。
抽象:每個實體都有很多的數據和行為,只提取咱們關心的數據和行為。
封裝:用抽象的結果來描述實體的類型,稱為封裝。在C++中,可以用結構來實現封裝,但出於跟C語言兼容,一般在C++中結構也只封裝數據,用class關鍵字來實現真正的封裝,稱為類。封裝好的類和結構都只是一個類型。
定義類:
class xxx{
成員變量或者成員函數的定義
};
結構與類的區別:結構默認就是簡單打包,內容對外是透明的,可以通過結構變量訪問它的任何一個成員。類默認是保密封裝,對外是不透明的,不允許通過變量直接訪問它的成員,除非是故意公開的(public)。一般公開的都是函數。
這種類或者結構類型的變量(實例)就稱之為對象。
成員函數如果在外面去實現(函數體不寫在類內部),那麼函數名應該寫成 “ 類名::函數名 ”。結構也是如此。
文件結構:一個類一般寫成一個.h文件和一個.cpp文件,在.h文件中,成員函數只有聲明,沒有函數體,在.cpp文件中包含.h文件並且給那些成員函數的定義(包括函數體)。
24、創建一個對象時會自動調用一個成員函數,稱為構造函數。函數名就使用類名,無返回類型。構造函數可以重載,一般訪問限制為公開。創建對象如果不傳參數,不要帶空括號,否則被誤認為是一個函數聲明。
如果一個類沒有構造函數,編譯器會自動為它產生一個不干事的空函數。只要有構造函數了,編譯器就不在為這個類產生這個無參構造函數(默認構造函數、缺省構造函數)。
25、一個對象創建時如果需要做額外的事情,就可以放在構造函數裡面。創建對象的實際操作步驟:分配內存,執行構造函數。如果成員是對象,成員的構造函數會在整體的構造函數之前執行。
26、this:調用成員函數時,編譯器總是自動悄悄把點(或指向)它的那個對象的地址作為一個隱含參數傳遞;在成員函數內部,用關鍵字this來接收這隱含參數,類型為 X * const ,表示當前對象的地址。this:固定指向當前對象,只存在與成員函數中,對哪個對象調用這個成員函數的時候,this就指向哪個對象。 *this 就代表當前對象。
27、靜態成員:屬於整個類的數據或者行為
靜態成員變量:所有這個類的對象公用一份,在類外面初始化,初始化時要在變量名前面用類名雙冒號修飾。
靜態成員函數:所有這個類的對象共同的行為,不依賴於任何一個對象,因此靜態成員函數中不存在this,只能訪問靜態成員,訪問非靜態成員必須要指明對象。
28、初始化列表:
在構造函數定義裡緊跟參數表之後可以有一個初始化列表,用冒號開頭,後面有若干對“成員變量(初始值)”,多對之間用逗號分隔。初始化列表是初始化常量成員和引用成員的不二法門。
29、動態創建和釋放對象
new 類名 delete 地址
new 類名 (參數) delete 地址
new 類名 [ 個數 ] delete [ ] 地址
返回的都是地址。delete p ; p = NULL ;
一個變量的創建和釋放:
全局變量:main執行之前創建,main返回後釋放
靜態局部變量:第一次執行到時創建,main返回後釋放
普通局部變量(含形參):每次執行到時創建,超出作用范圍時釋放
臨時結果:臨時創建,當場使用,立即釋放
動態變量:執行new時創建,執行delete時釋放
-----------------------------------------------------復習-----------------------------------------------------
1、面向對象:封裝(寫類),前提是抽象。從對象、行為/交互角度去考慮程序如何實現。
類定義:數據、函數、訪問限制(public允許本類之外的函數訪問,private只允許本類的成員函數訪問)。多文件,類實現放在 .cpp 文件,類定義裡面函數只聲明(放在 .h 文件中#ifndef)。
構造函數:創建每一個對象時總會自動調用構造函數,可以重載。如果沒有人為定義構造函數,編譯器會自動產生一個無參的。
this :固定指向當前對象,只存在於成員函數中,對哪個對象 調這個成員函數的時候,this 就指向哪個對象。 *this 就代表當前對象。
2、靜態成員:屬於整個類的數據或者行為
靜態成員變量:所有這個類的對象公用一份,在類外面初始化。初始化時要在變量名前面用類名雙冒號修飾。
靜態成員函數:所有這個類的對象共同的行為,不依賴於任何一個對象,因此靜態成員函數中不存在this,只能訪問靜態成員訪問非靜態成員必須要指明對象。
3、析構函數:對象釋放時會自動調用的函數,函數名為 ~類名,總是無參,無法重載,一般也是公開的,無返回類型。
默認析構函數:如果一個類沒有定義析構函數,編譯器會自動產生一個什麼也不做的析構函數。
如果想在main之前或者main之後做一些工作,可以用全局對象的構造函數和析構函數來實現。
對象本身占用的內存空間會在超出作用范圍時或者delete時自動釋放了,如果對象還額外分配了資源,可以在析構函數中釋放那些額外的資源。
-----------------------------------------------------------------------------------------------------------------
30、匿名對象:直接定義一個對象但不給提供名字,這個對象會在這個語句之後就立即釋放而且編譯器能對匿名對象的使用進行最大限度的優化性能。提倡使用匿名對象,前提是它是一次性對象。
類名 變量名(參數);
類名 (參數);(匿名對象聲明,沒有變量名,對象只能當場使用。)
只有一個參數時,匿名對象可以看成類型轉換。
如果要禁止自動用匿名對象實現類型轉換,可以用explicit修飾構造函數。
31、拷貝構造:
零初始化:類型名(),對於基本類型而言是數值0,對於類或者結構類型而言是匿名對象。
當用一個同類對象初始化一個同類的新對象時,調用的自然是拷貝構造函數。一個類如果沒有定義拷貝構造函,編譯器會自動產生一個拷貝構造函數,內容是逐個復制對應的成員。這一般能滿足程序的需求。但如果其中有指針成員指向動態內存時,兩個對象的指針成員都會指向相同的地方,兩個對象釋放時都會delete這塊內存從而導致錯誤。要避免這個問題,需要自己寫拷貝構造函數,讓每個對象指針成員各自指向一片動態內存,把舊動態中的數據復制過去。讓兩片動態內存中的數據相同。
32、復習
小點心:各種增強(強類型,重載,引用/復制)
面向對象:封裝、抽象、類(定義,實現)類型,對象,構造函數、析構函數、默認什麼都不干,成員函數。私有數據,公開函數。成員函數中有this指針指向用來調用這個成員函數的對象。
靜態成員函數/變量屬於整個類,訪問時不需要通過對象,直接用“類名::成員”,所以沒有this。靜態成員在類外面初始化(類似全局變量但要用“類名::成員”格式。)
初始化列表:在構造函數的定義的參數表之後函數體之前,冒號開頭,若干對“成員(初始值)”,逗號隔開。成員如果是常量或者引用只能用初始化列表。
匿名對象:不起名的對象,同樣調用構造函數和析構函數。
一個參數的匿名對象可以看成類型轉換,編譯器可以自動完成這樣的類型轉換。用explicit修飾構造函數可以禁止編譯器通過它來自動進行類型轉換。
new/delete 在堆空間創建對象,不會自動釋放,必須要認為釋放。
注意配對:new與delete,new[ ]與delete[ ]配對,特別注意new/delete不要跟malloc/free交叉配對。
拷貝構造函數:創建新對象用另一個同類對象初始化它時,調用拷貝構造函數(形參是本類類型的const引用)。一般默認的就不可以了。如果有指針成員指向動態內存,必須要自己來定義拷貝構造函數實現需要的功能,特別是指針成員不要直接復制,而讓他們各自指向一塊不同的動態內存,然後復制動態內存中的數據。
33、面向對象的擴展功能
備注:編譯器在編譯一個類時,會先掃描類定義(不含函數體),之後才掃描類實現(各成員函數的函數體,靜態成員變量的初始化),所以在類的成員函數裡可以訪問在後面才出現的成員。
const對象調用的成員函數也要求不會修改成員變量的數據。成員函數可以再參數表後用const來宣稱自己不會修改當前對象的數據,稱const成員函數。因此,用const對象只能調用const成員函數。
const對象內部如果確實有需要修改的數據成員,用mutable來修飾即可。
34、 運算符重載:在C++中,運算符都被當成函數。C++允許程序員自己來規定運算符如何工作,方法就是自己定義相應的運算符函數,比如 operator &,operator +,operator <<,稱為運算符重載。運算符函數代替運算符,其中函數參數對應運算符的操作數,函數返回值對應運算符的運算結果。
35、 運算符函數的形參和返回類型盡量用引用,盡量加const。運算符函數的定義格式之一:全局函數
返回類型operator運算符(參數表)
雙目:運算結果的類型operator運算符(操作數1,操作數2);
單目:運算符結果的類型operator運算符(操作數)。
36、在全局函數中如果需要訪問對象的非公開成員,需要在類中對這個全局函數進行授權,方式是在類裡用friend聲明這個函數為友元。一般在類裡面聲明在外面定義,也可以在類裡面friend聲明的地方直接定義,但它依舊是友元,不是成員。
如果需要的話,一個類A也可以把另外一個類B聲明成友元,這等同於把B類的所有成員函數都聲明為A的友元了。一般不這樣做,而且友元聲明是單向且不傳遞的。
37、運算符函數的定義格式之二:成員函數
返回類型operator運算符(除第一個操作數之外的參數表)
雙目:運算結果類型operator運算符(第二個操作數)
單目:運算結果類型operator運算符()
以成員函數形式定義時,第一個操作數作為了當前對象,不需要作為實參傳遞,只需要傳遞剩余的操作數就可以了。在運算符函數裡可以通過*this或者this->來訪問第一個操作數。
規定只能用成員函數的[ ]、( )、=、->、強制類型轉換。
如果有指針成員指向動態內存,還應該自己寫賦值運算符函數來實現跟拷貝構造函數相似的功能。三大:拷貝構造、賦值、析構。
38、類型轉換運算符函數格式為
operator 類型名()
不寫返回類型,返回類型跟“類型名”一致,只能是成員函數。
圓括號運算符時,參數個數不定。函數定義格式:
返回類型operator()(參數表)
支持圓括號運算符的對象也稱為函數對象,因為使用的形式特別像函數。
39、復習
運算符重載:
編譯器把運算符當作函數
全局函數
函數名:operator 運算符
參數表:所有的操作數
返回類型:合乎情理即可
成員函數
函數名:operator 運算符
參數表:除第一個之外的操作數,當前對象作為第一個操作數
返回類型:合乎情理即可
編譯器會嘗試兩種形式([ ]( )=->type只當成員函數),如果兩種都有或者都沒有,編譯失敗。每個運算符函數只允許有其中一種形式。
程序員可以通過運算符函數自己設計運算過程(函數體)以及拿什麼作為運算結果(return的數據)。
. . . operator + (const A& x, int n);
A obj ; obj + ' * '
operator = 函數不寫也可以使用,默認逐個成員賦值,如果有指針成員指向動態內存是需要自己來寫。
operator type ( ) 不寫返回類型,卻勝寫返回類型,因為函數名裡面已經包含了返回類型。
ostream operator << (ostream &o, const A& x)
istream operator >> (istream &i, const A& x)
這兩個只能寫成非成員函數,如果需要訪問對象的私有成員,就要在類裡面把他們聲明為友元friend 。
const 對象只能調用const成員函數(const在參數表後)。
const成員函數裡的this是const A* const ,而普通成員函數裡的this是 A * const。const對象中的mutable修飾的數據成員允許修改。
const int *p;//p指向const int
const const *;//同上
int * const p;//p本身是const
const int * const p;//p本身是const而且指向const int
40、運算符重載
-> 返回內部某個結果變量的地址,編譯器通過這個地址執行真正的 -> 操作訪問裡面的成員。
new/delete 重載負責內存的分配和釋放,編譯器會在內存分配之後調用構造函數,內存釋放之前調用析構函數。有[ ]和沒有[ ]的是不同的運算符。
++/--
前++前--,正常單目運算符用法,因為計算結果就是對象的最新值,因此可以直接拿這個對象作為計算結果
成員:A& operator ++ ( ) { . . . return *this; }
友元:A& operator ++(A& x){. . . return x;}
後++後--,非正常用法,計算結果是對象的舊值,必須用臨時空間來保存,不能拿對象本身作為計算結果,定義運算符函數時需要一個多余的int啞元。
成員:A operator ++ ( ) { . . . return old; }
友元:A operator ++(A& x){. . . return old;}
運算符重載一定要合乎情理。運算符重載是自己規定運算符對於特定類型的操作數應該如何工作,對於基本類型的運算符不應該操作,不應該創造新的運算符,不應該改變操作數的個數。
有些運算符不允許重載: [ . ] [ .* ] [ ? : ] [ : : ] [ typeid ] [ sizeof ]