第一章 開始
1. C++中標准的頭文件是不帶.h後綴的。如下代碼值得注意:
#include
void sayHello()
{
cout<<"Hello World!";
}
void main()
{
sayHello();
}
#include
using namespace std;
void sayHello()
{
cout<<"Hello World!";
}
void main()
{
sayHello();
}
#include
void sayHello()
{
std::cout<<"Hello World!";
}
void main()
{
sayHello();
}
引用帶.h的頭文件時,不需要使用namespace。引用標准的不帶後綴名的頭文件時,需要使用namespace。使用namespace有兩種方式:1.通過using關鍵字調用。2.在具體的函數前通過::調用。例如std::cout。
2. #include稱之為預處理器,有兩種形式:<>表明文件時標准頭文件,查找過程會檢查預定義的目錄,“”引用的表示該文件時用戶提供的頭文件,查找文件從當前目錄開始,當前目錄沒有就查找預定義目錄。通過如下代碼防止重復處理:
#ifndef BOOKSTORE_H
#define BOOKSTORE_H
//content
#endif
使用如下代碼判斷預處理器常量是否已經被定義:
int main()
{
#indef DEBUG
cout<<”Beginning execution of main()”;
#endif
cout<<”Hello”;
}
如果定義了DEBUGE預編譯器常量,那麼會編譯cout<<”Beginning execution of main()”;如果沒有定義DEBUGE,不編譯此句。
3. 基本的輸入輸出流有:cin、cout和cerr。換行輸出為cout< 第二章 浏覽 1. 在C++中,數組不能直接賦值。而在Java中,數組時可以直接賦值的。 2. 在C++中分為靜態(在棧中)和動態(在堆中)分配內存。靜態分配在編譯時分配,效率高但缺乏靈活性;動態分配在執行時刻分配,效率低但靈活性高。靜態對象是有名字的變量,我們直接對其進行操作;動態對象是沒有名字的對象,我們通過指針對其進行操作。靜態對象的分配和釋放由編譯器自動處理;動態對象的分配和釋放由程序員顯式管理,使用的是new和delete。 int *pia = new int(1024);//pia指向單一對象的地址 int *pint = new int[4];//pint指向擁有四個元素數組的第一個元素的地址 delete pia;//刪除單一對象 delete []pint;//刪除數組 3. 調用對象的方法時,點操作符用於類對象調用,->用於對象指針的調用。 4. 訪問函數的開銷比直接訪問內存的開銷大。通過對象的方法訪問對象的變量時(訪問對象的方法是訪問函數,訪問對象的變量直接訪問內存),C++通過inline機制,將調用函數在調用點展開,節省了開銷。 5. 引用是沒有指針語法的指針。 6. 類的構造函數用來初始化對象的數據成員,析構函數負責釋放對象生命周期內申請到的所有資源。 7. 在繼承操作中,基類中需要在子類實現的函數應使用virtual修飾。C++支持多繼承。繼承關系反映的是is-a的關系。組合關系反映的是hava-a的關系。 第三章 C++數據類型 1. 基本數據類型: char(1bit)、short(2bit)、int(4bit)、long(4bit)、float(4bit)、double(8bit) 其中char、short、int和long為整型,分為有符號和無符號,默認為有符號。文字常量,例如數字1,其值不能改變,並且不可尋址。相比於文字常量,變量是可以尋址的,每個變量有兩個值與其對應,分別是數據值和地址值。 2. Class A;是聲明,聲明不是定義,不引起內存分配。 A a(100);靜態分配 A *a = new A(100);動態分配 3. C++中的關鍵字: 關鍵字explicit可以禁止“單參數構造函數”被用於自動類型轉換 class Stack { explicit Stack(int size); }; 沒有explicit的話Stack s = 40;能編譯通過 而有explicit則不能,必需Stack s(40); 關鍵字mutable意為:可變的。用於在const的函數裡面修改一些跟類狀態無關的數據成員 關鍵字volatile被設計用來修飾被不同線程訪問和修改的變量。volatile的作用: 作為指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值. 4. 基本數據類型變量的初始化:如果該基本類型的變量是在全局域定義的,系統初始化其值為0,如果是局部域或者通過new定義的,系統不對其初始化。 5. 對象的初始化支持兩種方式。1.int a=100;2.inta(100); 6. 空指針(void*)可以接受任何指針類型的地址值賦值,它表明該值是個地址值,但是該地址值的對象類型不知道,不能知道該指針覆蓋的內存區域。 7. C++提供兩種風格的字符串類型,一種是老式的C風格字符串,另一種是標准的C++引入的字符串string。使用老式的C風格字符串需要#include 8. 對於字符串的賦值。s2=s3;過程如下:首先將s2關聯的字符存儲區釋放掉,然後分配足夠存儲與s3相關聯的字符的存儲區,最後將與s3相關聯的字符拷貝到該存儲區中。注意這裡與Java的區別。 9. const定義變量為常量,使得變量的值不能被修改。常量必須初始化。非const對象的指針不能指向const對象。const指針可以指向非const變量,此時也不能通過指針修改其值。 A.const int*p=0;//(自右向左)p是一個指向int類型的,被定義為const對象的指針。稱之為:指向const的指針。指向const的指針可以指向其他,但是不能改變指向的值。const
int *p 等價於 int const *p。(const關鍵字位於第一、第二位置都是指向const的指針) const double minWage = 0; p=&minWage; *p=100;//×指向const的指針可以指向其他,但是不能改變指向的值 double dval=3.14; p=&dval; *p=100;//×指向const指針可以指向其他,但是不能改變指向的值 指向const的指針常用在函數的參數中,例如int strcmp(const char *str)用於保證被傳遞到函數中的實際對象在函數中不被修改。 B.interr=0;int *const cur=&err;//cur是指向int類型的const指針。稱之為:const指針。const指針不能指向其他的地址值,但是可以修改其值。 C.const double pi=3.14;const double *constpi_ptr=π指向const類型的const指針。 const int aa = 100; int bb = 200; int cc = 300; //指向const的指針 const int *p = &aa; //*p=100;錯誤 p=&bb; //*p=100;錯誤 //const指針 //int *const pp = &aa;錯誤,非const類型的指針不能指向const常量 int *const pp = &bb; //pp=&cc;錯誤 *pp = 100; int const * ppp = &aa; ppp = &cc; 10. 引用是沒有指針語法的指針,常用語函數的形參,將類對象傳遞給函數。引用必須初始化。引用一旦定義,不能指向其他對象。指針所有的操作都被應用在它所指向的對象上。 int a = 100; int&p = a;(指向int型的引用) int *pi = &a;int *&ppi = pi;(指向指針的引用,&和*作用抵消) 指向const的引用可以使用文字常量初始化,例如:const int &p = 3.14;其內部實現是通過為文字常量定義一個變量,然後引用指向該變量進行實現的。int
*pi=0;內部其實為:int tmp=0; const int &ri=tmp;而對於非指向const的引用,由於文字常量不能尋址,因此不能這樣初始化(int
&p=1;×)。 對於const intival = 1024;定義指向ival指針的引用如下: const int *const&p = &ival;(第一個const說明指向的是常量,第二個const說明是const指針,只能指向1024的地址,因為文字常量的地址是固定的) 引用與指針的區別; 1. 引用必須總是指向一個對象,必須初始化。指針可以為空。 2. 引用之間相互賦值時,改變的是被引用的對象而不是引用本身。 int &ri = ival, &ri2=ival2;ri=ri2;改變的是ival,而不是引用本身。賦值之後兩個引用依然指向原來的對象。 11. 枚舉:用於指定一個對象含有指定的幾個值。Enum Forms{a=2,b,c}.c=4. 12. 數組維數值必須是常量表達式,必須能在編譯期間計算出其值。顯式初始化的時候可以不寫維數值。字符串也是一個數組,但是其包含了額外的“\0”。數組不能被另一個數組初始化,也不能賦值給另一個數組。C++不提供編譯器對數組的邊界檢查。 13. 數組與指針:數組標示符代表數組中第一個元素的地址,它的類型是數組元素類型的指針。 14. vector可以被另一個vector初始化,可以賦值給另一個vector。 15. typedef的一道易錯題; Typedef char *cstirng; Extern const cstring cstr; 問cstr是什麼? 不是指向const字符的指針const char *cstr,而是指向char的const指針char
* const cstr。 16. volatile修飾符的作用是告訴編譯器,該對象的值可能在編譯器未監測到的情況下被改變。因此編譯器不能武斷地對引用這些對象的代碼進行優化處理。 17. pair提供key-value形式的存儲結構,使用需包含 第四章 表達式 1. %取模運算符號只能應用於整型操作數上,例如char、short、int和long。 2. 關系和邏輯運算符如果應用在整數值的上下文環境中,會自動提升為1或者0. 3. 不能使用諸如a[index--]
4. 利用i++和++i實現Stack。 Push:stack[top++] = value; Pop:value = stack[--top]; 5. sizeof操作符的作用是返回一個對象或類型名的長度。當使用sizeof計算數組時,返回的是數組的整個類型長度,而不是第一元素的長度。Sizeof計算指針時,sizeof(p)=4;siziof(*p)=p指向內存元素的類型大小。Sizeof計算引用時,返回的是引用的類型大小。sizeof
a和sizeof(a)兩種方式都正確。 size_t st; st = sizeof st; std::cout<<"st="< double *sp = new double[3]; int as[] = {1,2,3,4}; std::cout<<"sizeof(sp)="< std::cout<<"sizeof(*sp)="< std::cout<<"sizeof(as)="< std::cout<<"sizeof(double *)"< std::cout<<"sizeof(double &)"< 輸出結果為:4、8、16、4、8 此外:sizeof(‘c’)=1;sizeof(“c”)=2;【包含\0】sizeof(std::string)=16;string str =”s”;sizeof(str)=16; 6. 系統為程序分配內存的區域稱之為空閒存儲區或者堆。使用new執行分配內存的操作。所有從堆中分配的對象都是未命名的,new表達式返回的不是對象本身,而是對象的地址,對象所有的操作都是通過地址值間接完成的。使用delete來刪除我們從堆中申請的對象。刪除單個元素:delete p;刪除數組:delete[] p; 7. inta = 3;int b = a + 1.24;首先將a提升為double,然後a+1.24得到double的結果,然後將結果轉換為int(編譯器會報錯)。發生隱形轉換的情況如下: 1. 混合運算:int a= 3; double d = 3.243; double dd = a + d; 2. 用一種類型的表達式賦值給另一種類型的對象。 a = d;或者 int *p =0; 3. 把一個表達式傳遞給一個函數調用,表達式類型與參數類型不同。Extern double sqrt(double); cout< 4. 從一個函數返回一個表達式,表達式與返回類型不同。Double dif(int a,int b){return a – b;} 8. 算數運算中: 1. 類型總是被提升為較寬的類型。Int->float->double->long double 2. 小於整形的有序類型的算數表達式都轉換成整形。例如char、signed char short等。此外還有enum中的值,boo值也被提升為整形int。 9. 顯式類型轉換常用命令:static_cast(靜態轉換,一般的轉換)、dynamic_cast(運行時刻識別由指針或引用指向的類對象)、const_cast(const常量轉換)和reinterpret_cast。顯示類型轉換的語法為:castname 舊式強制轉換語法: 1. C++強制轉換符號:type(expr) 2. C語言強制轉換符號:(type)expr; 10. 任何非const數據類型的指針都可以被賦值為void*。 第五章 語句 1. string*p, p1;定義p為指針,p1為字符串。string *p,*p1;定義p,p1為指針。 2. switch如果不加break,則會從入口點一直執行。 3. break:跳出循環體;continue:跳出本次循環,轉而進行洗一次循環。