1. 程式人生 > >《王道》第8章 類

《王道》第8章 類

                                        《王道》第8章 類

1 訪問標號

    訪問標號public、private和protected。

    如果類是用struct關鍵字定義的,則在第一個訪問標號之前的成員是公有的;如果類是用class關鍵字定義的,則這些成員是私有的。

    類對其成員的訪問形式主要有以下兩種:

    1)內部訪問:由類中的成員函式對類成員的訪問。

    2)物件訪問:在類外部,通過類的物件對類的成員的訪問。

    類的成員可以有public、private和protected三種屬性,類的成員函式(內部訪問)以及友元函式可以訪問類中所有成員

,但在類外通過類的物件(物件訪問),就只能訪問類的公有成員。未考慮有繼承的情況。

2 成員函式

    在類內部,宣告成員函式是必需的,而定義成員函式則是可選的。在類內部定義的函式預設為inline(行內函數)

    呼叫成員函式時,每個成員函式(除了static成員函式外)都有一個額外的、隱含的形參this。在呼叫成員函式時,形參this初始化為呼叫該函式的物件的地址。

3 建構函式

3.1 建構函式

    建構函式是特殊的成員函式,和類同名,且無返回型別

    建構函式在定義類物件時自動執行。

    一個類可以有多個建構函式,即建構函式可過載,每個建構函式必須有與其他建構函式不同數目或型別的形參。

    建構函式可以是行內函數。當建構函式的函式體寫在類的定義體內,建構函式就是內聯的。

    如果沒有為一個類顯式定義任何建構函式,編譯器將自動為這個類生成預設建構函式(預設建構函式是不帶引數的建構函式,或所有的形參都有預設實參的建構函式)。

    如果定義了其他建構函式,則提供一個預設建構函式幾乎總是對的。

3.2 成員初始化列表

    參考部落格

3.3 拷貝建構函式/複製建構函式

    參考部落格

    (1) 什麼是拷貝建構函式?

    拷貝建構函式是一種特殊的建構函式,它在建立物件時,是使用同一類中之前建立的物件來初始化新建立的物件。

    拷貝建構函式只有單個形參,且該形參是對本類型別的物件的引用。

    (2) 哪些情況下,會呼叫拷貝建構函式?

    * 當函式的引數為類的物件時;

    * 函式的返回值是類的物件 ;

    * 物件需要通過另外一個物件進行初始化;

    (3)為什麼拷貝建構函式的引數是一個引用,可以不是引用嗎?

    簡單的回答是為了防止遞迴引用

    具體一些可以這麼講:

    當一個物件需要以值方式傳遞時,編譯器會生成程式碼呼叫它的拷貝建構函式以生成一個複本。如果類A的拷貝建構函式是以值方式傳遞一個類A物件作為引數的話,當需要呼叫類A的拷貝建構函式時,需要以值方式傳進一個A的物件作為實參; 而以值方式傳遞需要呼叫類A的拷貝建構函式;結果就是呼叫類A的拷貝建構函式導致又一次呼叫類A的拷貝建構函式,這就是一個無限遞迴。

    (4)淺拷貝和深拷貝

    淺拷貝只是對指標的拷貝,拷貝後兩個指標指向同一個記憶體空間,深拷貝不但對指標進行拷貝,而且對指標指向的內容進行拷貝,經深拷貝後的指標是指向兩個不同地址的指標。

    簡單的來說就是,在有指標的情況下,淺拷貝只是增加了一個指標去指向已經存在的記憶體,而深拷貝就是增加一個指標並且申請一個新的記憶體,使這個增加的指標指向這個新的記憶體,採用深拷貝的情況下,釋放記憶體的時候就不會出現在淺拷貝時重複釋放同一記憶體的錯誤!

3.4 解構函式

    解構函式是一個特殊函式,它可以完成所需的資源回收,作為類建構函式的補充。當物件超出作用域或動態分配的物件被刪除時,將自動應用解構函式。不管類是否定義了自己的解構函式,編譯器都自動執行類中非static資料成員的解構函式。

    一般來說,如果類中定義了虛擬函式,建構函式也應被定義為虛解構函式,尤其是類內有申請的動態記憶體,需要清理和釋放的時候。

3.5 建構函式與解構函式呼叫順序

參考部落格1

參考部落格2

4 操作符過載

4.1 賦值操作符過載

    參考部落格

    程式碼

#include<iostream>
#include<string>
using namespace std;

class String {
public:
	String();    //預設建構函式
	~String();   //預設解構函式
	String& operator = (const String &str);   //賦值運算子過載函式
private:
	char* data;
};
String& String::operator = (const String &str) {      // 返回值的型別宣告為該型別的引用,只有這樣,才能允許連續賦值
	                                                  //傳入的引數為常量引用,避免修改傳入例項的狀態,以及避免形參到實參呼叫一次拷貝建構函式,提高程式碼效率
	if (this == &str)                                // 避免自賦值 
		return *this;
	delete[]data;                                    //釋放例項自身已有的記憶體,防止記憶體洩漏
	data = NULL;
	data = new char[strlen(str.data) + 1];
	strcpy(data, str.data);
	return *this;
}

    複製建構函式與賦值運算子的區別是什麼?

    首先要說明的是,若使用者沒有定義,C++隱式宣告一個複製建構函式和一個賦值運算子。二者很像,但是在下面這點上有很大不同:複製建構函式只在物件例項化時才會被呼叫,也就是說,在複製建構函式呼叫期間,這個物件處於一個未決狀態(直到複製建構函式被成功呼叫),另外,複製建構函式不返回任何值,void都沒有。而賦值運算子則在一個現存的物件被賦予新的值時被呼叫,並且它有返回值。

    下列函式可以被繼承嗎?

    建構函式、解構函式、複製建構函式和賦值運算子過載函式都不能被派生類繼承。

    C++中的空類預設產生哪些成員函式?

    建構函式、複製建構函式、解構函式、賦值運算子過載函式、取址運算子過載函式、const取址運算子過載函式。

4.2 輸出操作符<<的過載

4.3 operator new 與 operator delete的過載

    參考部落格

    練習:以下程式碼中,A的建構函式和解構函式分別執行了()次?

A *pa = new A[10];
delete[]pa;

    解析:10,陣列pa中共有10個A類物件,每個物件都要執行建構函式和解構函式。

    疑問:如何限制棧物件的生成?如何限制堆物件的生成?(待理解)

5 成員函式的過載、覆蓋和隱藏

參考部落格

對類層次的同名成員函式來說,有3種關係:過載(overload)、覆蓋(override)和隱藏(hide、oversee)。

1.成員函式的過載

    成員函式被過載的特徵:

    1)相同的範圍(在同一個類中);

    2)相同的函式名字;

    3)不同的引數列表;

    4)virtual關鍵字可有可無。

2.成員函式的覆蓋/重寫

    成員函式被覆蓋的特徵:

    1)不同的範圍(分別位於派生類和基類);

    2)相同的函式名字;

    3)相同的引數;

    4)基類函式必須有virtual關鍵字。

3.成員函式的隱藏

    隱藏指的是在某些情況下,派生類中的函式遮蔽了基類中的同名函式,這些情況包括:

    1)兩個函式引數相同,但基類函式不是虛擬函式。和覆蓋的區別在於基類函式是否是虛擬函式。

     2)兩個函式引數不同,無論基類函式是否是虛擬函式,基類函式都會被遮蔽。和過載的區別在於兩個函式不在同一類中。