使用類對象的指標和引用——特別是引用,在物件導向編程和函數形參說明方面——特別是後者,非常重要。類對象可能涉及相當多的資料,因此使用按值傳遞機制(將函數形參指定為對象)可能非常耗時和低效,因為需要複製每一個實參對象。還有一些對類的某些操作而言必不可少的技術也需要使用引用。比如稍後將看到的那樣,如果不使用引用形參,我們將不能編寫複製建構函式。
類對象的指標我們以聲明其他指標的相同方式,聲明指向類對象的指標。例如,下面這條語句聲明了一個指向CBox類對象的指標:CBox* pBox = 0; // Declare a pointer to CBox現在,我們可以在指派陳述式中以通常的方式,使用該指標來儲存CBox對象的地址:pBox = &cigar; // Store address of CBox object cigar in pBox正如在Compare()成員函數的定義中使用this指標時所看到的那樣,我們可以使用對象的指標來調用函數。例如,我們可以像下面語句中那樣通過指標pBox調用函數Volume():cout << pBox->Volume(); // Display volume of object pointed to by pBox該語句再次使用了間接成員訪問運算子。大多數程式員在這種情況下通常都使用該運算子,因此從現在開始,本書也將大量使用。試一試:類對象的指標讓我們稍微深入地試著練習一下間接成員訪問運算子的用法。我們將使用Ex7_10.cpp作為基礎,但需要作一些修改。// Ex7_13.cpp// Exercising the indirect member access operator#include <iostream>using std::cout;using std::endl;class CBox // Class definition at global scope{ public: // Constructor definition CBox(double lv = 1.0, double bv = 1.0, double hv = 1.0) { cout << endl << "Constructor called."; m_Length = lv; // Set values of m_Width = bv; // data members m_Height = hv; } // Function to calculate the volume of a box double Volume() const { return m_Length*m_Width*m_Height; } // Function to compare two boxes which returns true (1) // if the first is greater that the second, and false (0) otherwise int Compare(CBox* pBox) const { return this->Volume() > pBox->Volume(); } private: double m_Length; // Length of a box in inches double m_Width; // Width of a box in inches double m_Height; // Height of a box in inches};int main(){ CBox boxes[5]; // Array of CBox objects declared CBox match(2.2, 1.1, 0.5); // Declare match box CBox cigar(8.0, 5.0, 1.0); // Declare cigar Box CBox* pB1 = &cigar; // Initialize pointer to cigar object address CBox* pB2 = 0; // Pointer to CBox initialized to null cout << endl << "Address of cigar is " << pB1 // Display address << endl << "Volume of cigar is " << pB1->Volume(); // Volume of object pointed to pB2 = &match; if(pB2->Compare(pB1)) // Compare via pointers cout << endl << "match is greater than cigar"; else cout << endl << "match is less than or equal to cigar"; pB1 = boxes; // Set to address of array boxes[2] = match; // Set 3rd element to match cout << endl // Now access thru pointer << "Volume of boxes[2] is " << (pB1 + 2)->Volume(); cout << endl; return 0;}如果運行該樣本,則輸出看起來與下面顯示的大致相似:Constructor called.Constructor called.Constructor called.Constructor called.Constructor called.Constructor called.Constructor called.Address of cigar is 0012FE20Volume of cigar is 40match is less than or equal to cigarVolume of boxes[2] is 1.21注意:當然,您的PC上對象cigar的地址值有可能不同。
樣本說明對類定義的修改不是重要的實質性修改。我們只是將Compare()函數改為接受CBox對象的指標作為實參。另外,我們現在知道了關於const成員函數的資訊,又因為Compare()函數也不修改對象,所以將其聲明為const。main()函數只是以各種相當隨意的方式,練習使用CBox對象的指標而已。在main()函數中,我們在聲明過數組Boxes以及CBox對象cigar和match之後,聲明了兩個CBox對象的指標。第一個指標pB1被初始化為對象cigar的地址,第二個指標pB2被初始化為NULL。這兩條語句使用指標的方式與我們使用基本類型的指標時的方式完全相同。我們在使用自訂類型的指標這一事實沒有造成任何差別。我們使用pB1與間接成員訪問運算子,獲得被指向對象的體積,並將結果顯示出來。然後,將match的地址賦給pB2,並在調用比較函數時使用了pB1和pB2兩個指標。因為Compare()函數的實參是CBox對象的指標,所以該函數為實參對象調用Volume()函數時使用了間接成員訪問運算子。為了證實使用指標pB1選擇成員函數時可以執行地址算術,我們將pB1設定為CBox類型數組boxes中第一個元素的地址。之後,選擇數組的第三個元素,並計算其體積。結果與match的體積相同。從輸出可以看出,共有7次對CBox物件建構函數的調用,其中5次是建立數組Boxes,另外建立對象cigar和match各需要一次。總之,使用類對象的指標與使用基本類型(比如double類型)的指標之間實質上沒有任何區別。
類對象的引用當隨同類一起使用時,引用才能真正獲得應該得到的榮譽。如同指標的情況一樣,在聲明和使用類對象的引用與聲明和使用基本類型變數的引用之間,實質上沒有任何區別。例如,為了聲明對象cigar的引用,我們可以這樣寫:CBox& rcigar = cigar; // Define reference to object cigar為了使用引用計算對象cigar的體積,我們只需在應該出現對象名的位置使用引用名即可:cout << rcigar.Volume(); // Output volume of cigar thru a reference我們可能還記得,引用扮演著被引用對象別名的角色,因此其用法完全與使用原來的對象名相同。
1. 實現複製建構函式引用的重要性實際體現在函數(特別是類的成員函數)的實參和傳回值等上下文中。讓我們首先返回到複製建構函式的問題。目前,我們暫且迴避何時需要編寫自己的複製建構函式這個問題,而全神貫注於如何編寫一個複製建構函式的問題。我們將使用CBox類,這僅僅是為了使討論更具體。複製建構函式是通過用同類的現有對象進行初始化,從而建立新對象的建構函式,因此需要接受同類的對象作為實參。我們經過考慮,可能寫出下面的原型:CBox(CBox initB);現在,考慮調用該建構函式時將發生什麼事情。如果我們寫出CBox myBox = cigar;這樣一條聲明語句,那麼將產生如下所示對複製建構函式的調用:CBox::CBox(cigar);在意識到實參是通過按值傳遞機制傳遞的之前,該語句似乎沒有任何問題。在可以傳遞對象cigar之前,編譯器需要安排建立該對象的副本。因此,編譯器為了處理複製建構函式的這條調用語句,需要調用複製建構函式來建立實參的副本。遺憾的是,因為是按值傳遞,第二次調用同樣需要建立實參的副本,因此還得調用複製建構函式,就這樣持續不休。我們最終得到的是對複製建構函式的無窮調用。您肯定已經猜到了,解決方案是使用const引用形參。我們可以將複製建構函式的原型寫成下面這樣:CBox(const CBox& initB);現在,電腦再不需要複製複製建構函式的實參。實參是用來初始化引用形參的,因此沒有複製發生。我們記得前面關於引用的討論中說過,如果函數的形參是引用,則調用該函數時不需要複製實參。函數直接存取調用函數中的實參變數。const限定符用來確保該函數不能修改實參。注意:這是const限定符的另一個重要用途。我們應該總是將函數的引用形參聲明為const,除非該函數將修改實參。我們可以像下面這樣實現這個複製建構函式:CBox::CBox(const CBox& initB){ m_Length = initB.m_Length; m_Width = initB.m_Width; m_Height = initB.m_Height;}該複製建構函式的定義假定自己位於類定義外部,因此建構函式名用類名和範圍解析運算子加以限定。被建立對象的各個資料成員用傳遞給建構函式的實參對象的對應成員進行初始化。當然,我們也可以使用初始化列表來設定對象的值。