1、new和delete
int* pi = new int(0);//把值初始化為0A* pA = new A(); //對於自訂類型,建立對象分兩步,第一步分配記憶體,第二步調用建構函式 A()是建構函式。pA->function();delete pA; //對於自訂類型,第一步調用解構函式,第二步釋放記憶體。int *pi = new int[10];delete []pi; //申請10個元素的一維數組int **pi = new int[5][6];delete [][]pi;//申請一個二維數組。const int *pci = new const int(1024);
動態建立的 const 對象必須在建立時初始化,並且一經初始化,其值就不能再修改。delete p後,該指標變成懸垂指標,有可能指向記憶體的任何一個位置。一旦刪除了指標所指向的對象,立即將指標置為 0,即指向NULL,這樣就非常清楚地表明指標不再指向任何對象
2、malloc 和 free
動態記憶體管理容易出錯
1、刪除( delete )指向動態分配記憶體的指標失敗,因而無法將該塊記憶體返還給自由儲存區。刪除動態分配記憶體失敗稱為“記憶體流失(memory leak)”。記憶體流失很難發現,一般需等應用程式運行了一段時間後,耗盡了所有記憶體空間時,記憶體流失才會顯露出來。 delete後要檢測一下。
2、讀寫已刪除的對象。如果刪除指標所指向的對象之後,將指標置為 0 值,則比較容易檢測出這類錯誤。
3、對同一個記憶體空間使用兩次 delete 運算式。當兩個指標指向同一個動態建立的對象,刪除時就會發生錯誤。如果在其中一個指標上做 delete 運算,將該對象的記憶體空間返還給自由儲存區,然後接著 delete 第二個指標,此時則自由儲存區可能會被破壞。
3、const用法
1、定義後左值就不能被修改,所以定義時必須初始化。
const對象預設為局部變數,若想全域使用,必須顯示聲明extern。
檔案1 extern const int buffersize=512; //定義
檔案2 extern const int buffersize; //使用
2、const 引用是指向 const 對象的引用
const int ival = 1024;const int &refVal = ival; int &ref2 = ival; //n 非_常引用 指向指向了一個常對象const 引用可以初始化為不同類型的對象或者初始化為右值int i = 42; const int &r = 42; // legal for const references onlyconst int &r2 = r + i; double dval = 3.14;const int &ri = dval;
//編譯器會把這些代碼轉換成如以下形式的編碼: int temp = dval; // create temporary int from the doubleconst int &ri = temp; // bind ri to that temporary
如果 ri 不是 const,那麼可以給 ri 賦一新值。這樣做不會修改 dval,而是修改了 temp。期望對 ri 的賦值會修改 dval 的程式員會發現 dval 並沒有被修改。僅允許 const 引用綁定到需要臨時使用的值完全避免了這個問題,因為 const 引用是唯讀。
3、常指標,指標常量
const int* p1 = &i;//常量指標,指向一個常量的指標,等同於int const* p1 = &i;//*p1 = 2; //指標指向的內容不能改p1 = &j; //指標可以修改,可以指向別的變數int* const p2 = &i;//指標常量,不能修改p2。必須初始化。一個指標,是常量*p2 = 2; //指標指向的內容可以修改,//p2 = &j; //指標不能指向別的變數const int* const p3 = &i;//指向常量的常指標const int& ri = i;//常量引用,不能修改ri的值。 //ri = 10; i = 10; OK
4、常函數
//寫一個類的時候,儘可能多的聲明常函數。以滿足常量對象的調用需求。
int GetN() const //常函數,在函數內不能修改類的資料成員。
//編譯器解釋為 int GetN(const A* this)
//必須是類的成員函數常對象只能調用常函數。
const修飾形參,提高參數傳遞的效率,保證傳入參數不被修改!一般不要返回資料成員的地址或者引用,以免破壞類的封裝性。
const string& GetS() const {return s;} //用const修飾傳回值
4、static
int i; //執行個體成員,屬於對象,每個對象都有自己的一份拷貝,只能通過對象訪問
static int j; //靜態成員,屬於類,所有對象共用一份資料。可以通過對象名或者類名訪問。
int A::k = 0; //靜態成員需要在類外初始化
static void function(); //靜態成員函數:無this指標,不能訪問類的執行個體成員,只能訪問類的靜態成員。
5、初始化
int ival(1024); // 直接初始化則是把初始化式放在括弧中【更靈活效率更高】
int ival = 1024; // 複製初始化文法用等號(=)
初始化列表
A::A() : i(2),j(3),k(10){......}A::A(int a,int b):i(a),j(b){} //A的私人資料成員i,j
6、類
1.建構函式Student 的私人成員m_ID,m_cName
Student::Student(char *pName, int ssId){m_ID = ssId;strcpy(m_cName, pName);cout << "construct new student" <<endl;}
2.拷貝建構函式
Student::Student(const Student& Stu){m_ID = Stu.m_ID;strcpy(m_cName, Stu.m_cName);cout << "construct copy of" << Stu.m_cName << endl;}
3.淺拷貝 自己不定義,C++預設的拷貝建構函式深拷貝
Student::Student(Student& stu){cout <<"Constructing " <<stu.pName <<endl;pName=new char[strlen(stu.pName)+1];if(pName!=0){strcpy(pName, stu.pName);}}
7、網域名稱空間
namespace N1{void F(){}} //自訂useing namespace N1; F(); /調用
或者N1:: F();
8、函數參數預設
int add ( int x,int y = 5,int z = 4 ); Y int add(int x = 3,int y,int z); N
預設參數(default value)由後向前
9、傳遞指向指標的引用
void ptrswap(int *&v1, int *&v2) //int *&v1理解為v1是一個引用,與指向int型對象的指標相關聯。{ int *tmp = v2; //也就是說,v1 只是傳遞進 ptrswap 函數的任意指標的別名。 v2 = v1; v1 = tmp; } //使用的時候用指標ptrswap(pi1,pi2)
10、標頭檔
1、標頭檔用於聲明而不是用於定義
2、定義const 對象
3、inline函數
11、靜態聯編與動態聯編
calss A{public:void f(){cout<<"a";}};class B:public A{public:void f(){cout<<"b";}};class C:public A{public:void f(){cout<<"c";}};class D : public A{}; //沒有重寫,繼承父類的函數,也是抽象類別,不能執行個體化。(該例子是動態聯編的)void Test(A& ra){ra.f();//取決於調用f1的引用或者指標的宣告類型。叫做靜態聯編,也就是編譯時間已經確定了會執行哪個函數體。}void main(){B b;A& ra = b;ra.f(); //輸出aA* pa = &b;pa->f(); //輸出aTest (b); //輸出a //如果A類的前面加上virtual ,則變成動態聯編,這時輸出b;}
12、動態聯編條件:1.用基類的引用(指標)指向衍生類別的對象 2.只有調用虛函數,才有動態聯編的效果。
class A // 抽象類別:含有純虛函數的類。不能執行個體化。{public :virtual void f1() {cout << "A f1" << endl;}//父類中聲明純虛函數
};
如果一個類有可能作為基類,那麼需要把它的解構函式寫成虛函數。衍生類別的解構函式自動成為虛函數
13、指標
1、指標使用前要初始化!!!!
---->運行時的錯誤如果使用未初始化的指標,會將指標中存放的不確定值視為地址,然後操縱該記憶體位址中存放的位內容。使用未初始化的指標相當於操縱這個不確定地址中儲存的基礎資料。因此,在對未初始化的指標進行解引用時,通常會導致程式崩潰。
2、特殊的指標類型 void*,它可以儲存任何類型對象的地址
3、指向指標的指標
//定義: int *pi = &i; int **ppi =π
使用:解引用2次
cout<<**ppi<<endl;
4、指標和數組:
int *ip = ia; // ip 指向ia[0] ia是一個數組名。int *ip2 = ip + 4; // ok: 指標+4,則直至後移4個單位,然後賦值給指標ip2,注意不要越界
指標還支援減操作
int last = *(ia + 4) //取數組的第四個元素,解引用後賦值給lastint last = *ia + 4 //取數組第一個元素,解引用後+4
指標和下標
int *p = &ia[2]; // ok: p 指向數組ia的第2個元素int j = p[1]; // ok: p[1] 相當於 *(p + 1),即ia[3]int k = p[-2]; // ok: p[-2] 相當於ia[0]
指標遍曆數組 -------指標相當於數組的迭代器
const size_t arr_sz = 5;int int_arr[arr_sz] = { 0, 1, 2, 3, 4 };//pbegin指向數組第一個元素,pend指向最後一個元素for (int *pbegin = int_arr, *pend = int_arr + arr_sz;pbegin != pend; ++pbegin) cout << *pbegin << ' '; // 輸出當前對象
14、位操作符
unsigned char bit1 = 0227 //10010111 ~ bitwise NOT(位求反) bit2=~bit1; //01101000 << left shift(左移) bit1<<1 //00110110 整體左移,右側補0>> right shift(右移) bit1>>2 //00100101 整體右移,左側補0& bitwise AND(位與) 和bit逐位取與操作^ bitwise XOR(位異或) 和bit逐位取異或操作| bitwise OR(位或) 和bit逐位取或操作
15、自增自減運算子
後置++ a++;
cout<<*p++<<endl; 等價於 cout<<*p<<endl;++p;
16、使用預先處理進行調試
int main(){ #ifndef NDEBUG cerr << "starting main" << endl; #endif}
int main(){ #ifndef NDEBUG cerr << "starting main" << endl; #endif}如果 NDEBUG 未定義,那麼程式就會將資訊寫到 cerr(輸出到控制台)中。如果 NDEBUG 已經定義了,那麼程式執行時將會跳過 #ifndef 和 #endif 之間的代碼。