1. 程式人生 > >《C++面向物件程式設計》課程筆記 lessen1

《C++面向物件程式設計》課程筆記 lessen1

1. 引用

1 引用的三點注意事項:1. 定義引用時一定要將其初始化成引用某個變數。

                                    2. 初始化後,它就一直引用該變數,不會再引用別的變量了。從一而終。

                                    3. 引用只能引用變數,不能引用常量和表示式。

2 不能通過常引用去修改其引用的內容。

int n_c = 100;
	const int & r_c = n;
 // r_c = 200;           //編譯錯誤。不能通過常引用去修改其引用的內容。
	n_c = 300;           //編譯正常

3 常引用和非常引用的轉換

const T & 和 T & 是不同的型別!!!

 T & 型別的引用或 T 型別的變數可以用來初始化 const T & 型別的引用。

const T 型別的常變數和 const T & 型別的引用則不能用來初始化 T & 型別的引用,除非進行強制型別轉換。

2. const 關鍵字

1 不可通過常量指標修改其指向的內容。

int n,m;
	const int *p = & n;
//	*p = 5;             //編譯出錯。試圖通過常量指標改變其指向的值
	n = 4;
	p = &m;  //常量指標的指向可以改變

2 不能把常量指標賦值給非常量指標,反過來可以。

	const int *p1;
	int *p2;
	p1 = p2; //ok
//	p2 = p1; //error
	p2 = (int *)p1; //ok。強制型別轉換

3 函式引數為常量指標時,可避免函式內部不小心改變引數指標所指地方的內容。

void myprint(const char *p)
{
	//strcpy(p,"this");
    //編譯出錯。strcpy函式的第一個引數為 char * 型別,不能把 const char * 型別的 p 賦值給它
	printf("%s",p);  //ok
}

3. 動態記憶體分配

1 new 運算子

分配一個變數:P = new T;  

T 是任意型別名,P 是型別為 T * 的指標。動態分配出大小為 sizeof(T) 位元組的記憶體空間,並且將該記憶體空間的起始地址賦值給 P。比如:

int *pn;
pn = new int;
*pn = 5;

分配一個數組:P = new T[N];

T:任意型別名

P:型別為 T * 的指標

N:要分配的陣列元素的個數,可以是整數也可以是表示式。

動態分配出一片大小為 N * sizeof(T) 位元組的記憶體空間,並且將該記憶體空間的起始地址賦值給 P 。

int * pm;
int i = 5;
pm = new int[i*20];
pm[0] =10;
pm[100] = 30;   //編譯時沒問題,執行時導致陣列越界。0-99

new 運算子的返回型別:

new T 和 new T[N] 這兩個表示式的返回型別都是 T * 。

int * p = new int;

用“new”動態分配的記憶體空間,一定要用“delete”運算子進行釋放。

delete 指標;  //該指標必須指向 new 出來的空間

int *p = new int;
*p = 5;
delete p;
delete p;  //導致異常,一片空間不能被delete多次。

用 “delete” 釋放動態分配的陣列時要加 “[ ]” 。

delete [ ] 指標; //該指標必須指向 new 出來的陣列。如果沒有 [ ] 會造成 分配的動態空間沒有完全釋放掉。

int *p_t = new int[20];
p_t[0] = 1;
delete [] p_t;

4. 行內函數和過載函式

1 行內函數

函式呼叫是有時間開銷的,如:引數入棧、返回值入棧,當函式非常簡單且反覆執行多次時,開銷就會比較大。為了減少函式呼叫的開銷,引入了行內函數機制。 編譯器處理對行內函數的呼叫語句時,是將整個函式的程式碼插入到呼叫語句處,而不會產生呼叫函式的語句。缺點是編譯器的可執行語句可能增多。

在函式定義前面加 “inline” 關鍵字,即可定義行內函數。 

inline int Max(int a,int b)
{
	if(a>b)
		return a;
	return b;
}

2 過載函式

一個或多個函式,名字相同,然而引數個數或引數型別不同,這叫做函式的過載。 

以下三個函式是過載關係:

 (1)  int Max(double f1, double f2) { }

 (2)  int Max(int n1, int n2) { }

 (3)  int Max(int n1, int n2, int n3) { }

函式過載使得函式命名變得簡單。編譯器根據呼叫語句中的實參的個數和型別判斷應該呼叫哪個函式。

Max(3.4, 2.5);   //呼叫(1)

Max(2, 4);        //呼叫(2)

Max(1, 2, 3);   //呼叫(3)

Max(3, 2.4);    //error,二定義。

若兩個函式名字相同,引數個數和型別也相同,只是返回值不同,這兩個函式不叫函式的過載,叫重複定義。

3 函式的預設引數

C++中,定義函式的時候可以讓最右邊的連續若干個引數有預設值,那麼呼叫函式的時候,若相應的位置不寫引數,引數就是預設值。 

void func(int x1, int x2=2, int x3=3) 
{

}

func(10);    //等於func(10,2,3)
func(10,8);  //等於func(10,8,3)
func(10, , 8) //不行,只能最右邊的連續若干個引數預設

函式引數可預設的目的在於提高程式的可擴充性。即如果某個寫好的函式要新增新的引數,而 原先那些呼叫該函式的語句,未必需要使用新增的引數,那麼為了避免對原先那些函式呼叫語句的修改,就可以使用預設引數。

5. 類和物件的基本概念

1 C語言使用結構化程式設計

程式 = 資料結構 + 演算法

程式由全域性變數及眾多相互呼叫的函式組成。

結構化程式設計的不足:1. 函式與其操作的資料結構沒有直觀的聯絡。2. 沒有“封裝”和“隱藏”的概念,不易差錯。3. 重用。沒有已有功能的重用。

2 面向物件的程式設計

面向物件的程式 = 類 + 類 + ... + 類

面向物件的程式設計方法:

將某類客觀事物共同特點(屬性)歸納出來,形成一個數據結構(可以用多個變數描述事物的屬性);

將這類事物所能進行的行為也歸納出來,形成一個個函式,這些函式可以用來操作資料結構(這一步叫“抽象”)。(屬性和操作)

 然後,通過某種語法形式,將資料結構和操作該資料結構的函式“捆綁”在一起,形成一個“類”,從而使得資料結構和操作該資料結構的演算法呈現出顯而易見的緊密關係,這就是“封裝”。(將變數和操作函式“封裝”)

面向物件的程式具有“抽象”,“封裝”,“繼承”,“多型”四個基本特點。

物件所佔用的記憶體空間大小等於所有成員變數的大小之和。(不考慮成員函式)

3 私有、公有、保護關鍵字

設定私有成員的機制叫 “隱藏” 。 “隱藏” 的目的是強制對成員變數的訪問一定要通過成員函式進行,成員函式外部不能訪問類的私有成員。那麼以後成員變數的型別等屬性修改後,只需要更改成員函式即可。否則,所有直接訪問成員變數的語句都需要修改。

  • 沒有關鍵字的視為private

    class A {
        int a; // private
        int Fun(); //private
    public:
        int b; // public
    };
  • private 成員只能在成員函式內部訪問,public 成員可以在任何地方訪問

  •