精通類和對象
本文是基於大家已經知道類和對象的基礎上強化一些知識
對象中的動態分配內存:
如果在對象中動態分配了內存,就應當在析構函數中釋放該內存,所以此時需要自己編寫析構函數。
處理復制和賦值:
如果沒有自己編寫復制構造函數和賦值操作符,C++會幫你生成一個,這些編譯器生成的方法分別遞歸地調用對象成員上的復制構造函數或賦值操作符。不過對於基本類型(如int、double、指針),則會提供淺或位復制(賦值):只是從源對象直接將數據成員復制或賦值到目標對象。如果在對象中動態分配了內存,就會帶來問題。
針對上述問題:只要在類中動態地分配了內存,就應該編寫自己的復制構造函數提供內存的深復制。在復制構造函數中要復制所有數據成員,而不只是指針成員。
對一個動態分配了內存的對象賦值時,因為它已經得到了初始化,所以需要自己編寫賦值操作符,在分配新內存之前,需要對其已經分配的內存進行釋放。
有時在類中動態分配內存時,最簡單的做法就是禁止別人復制或對你的對象賦值,為此可以將復制構造函數或operator=標記為private,此時不必為這二者操作不提供實現。
綜上所述:只要內需要動態分配內存,就需要自己編寫復制構造函數、析構函數、賦值操作符。
不同類型的數據成員:
靜態數據成員:與類關聯而不是與對象關聯的數據成員,它是屬於類的,而不是屬於某個對象的。
const數據成員:此數據成員一經創建和初始化就不能改動了。
當常量僅用於類時,應當使用static const 數據成員而不是全局常量。而且這些常量必須在源文件中為其聲明空間,此時也是最後一次為其賦值的機會:
class SpreadsheetCell
{
。。。code。。。
public:
static const int kMaxHeight;
。。。code。。。
};
。。。.cpp。。。
const int SpreadsheetCell::kMaxHeight = 100;
如果statict const 成員變量是簡單類型,C++標准允許在類的定義文件中聲明這些變量並同時對其賦值。
引用數據成員:如果類的定義中需要一個引用數據成員,並且這個成員的類型是另外一個類,而且必須了解引用的類,而不是使用一個#include,我們就可以使用使用類的超前引用。
class SpreadsheetApplication;//超前聲明
class SpreadsheetCell
{
…code….
SpreadsheetApplication &app;
};
同時SpreadsheetCell的構造函數的初始化列表中必須為app初始化。在復制構造函數中也是如此。但是注意:一旦初始化了,就不能在修改它所引用的對象了。因此,不必再賦值操作符中嘗試對此引用賦值。
const 引用數據成員:常規的引用可以指向const對象,類似的,引用成員也可以指向const對象const SpreadsheetApplication &app;
還可以有static引用成員,static const 引用成員,不過一般很少使用。
深入了解方法:
靜態方法:此方法應用於整個類,而不只是某個對象。在類外對其實現是,不需要加上關鍵字static。它不是在某一個特定的對象上調用,所以沒有this指針,而且不能對一個特定對象執行來訪問其非靜態成員。實際上,靜態方法就相當於常規函數。唯一的區別就在於,它可以訪問類的private和protected靜態數據成員。
切記:不要再靜態方法中訪問非靜態成員。
調用時,需要使用作用域解析操作符。
const 方法:const對象就是值不能改變的對象。如果一個const對象或對const對象的應用,編譯器就不允許你在改對象上調用任何方法,除非可以保證所調用的方法不會修改任何數據成員。要保證一個方法不會修改任何數據成員,具體做法就是將方法本身用const關鍵字來標記。如類的一個成員函數:string getString() const; 在類外定義此方法是也要加上const關鍵字。
如果一個方法聲明為const,但它確實會修改數據成員,編譯器就會報錯。而且不能將靜態方法聲明為const,因為這是多余的,靜態方法根本就沒有相應的實例,因此它們不可能修改內部值。如果方法中每個數據成員都有一個const引用,此方法值為const就很有用,一旦修改就會報錯。
非const對象可以調用const和非const方法,不過const對象只能調用const方法。
應當養成習慣,將所有不會修改對象的方法都聲明為const方法,這樣就可以在程序中使用const對象的引用。
注意,const對象也可以撤銷,而且可以調用其析構函數。不要把析構函數標記為const。
如果在const方法中確實要修改某個數據成員,則可以在類定義中此數據成員前面加上mutable關鍵字。 如:mutable int value;
方法重載:
C++不允許僅基於返回類型重載一個方法名,因為在許多情況下,編譯器無法確定要調用哪一個版本下的方法。
不過可以給予const重載一個方法。也就是說可以編寫二個同名的且參數相同的方法,一個聲明為const,另一個則不是。如果你提供一個const對象,則會調用const方法,如果你提供一個非const對象,則調用非const方法。
默認參數:
C++提供一個類似於方法重載的特性,稱為默認參數。可以在原型中為函數和方法參數指定默認值。如果用戶指定了實參,則忽略默認值。如果沒有指定實參,則使用默認值。默認參數在構造函數中最有用。
注意:只能從最右參數開始的一個連續參數表提供默認參數。否則,編譯器將無法將未提供的實參與默認值匹配。
注意:只是在方法聲明中指定默認參數,在定義中並不指定。如果想同時聲明一個默認構造函數,以及一個多參數默認構造函數且其所有參數都有默認值,則編譯器會報錯。因為如果沒有指定任何參數,它就不知道該調用哪一個構造函數。
內聯方法:
在C++中,你可以建議某個方法或函數調用實際上不應該當作方法或函數調用。實際上,編譯器應當把這個方法或函數體直接插入到代碼中方法或函數調用所在的位置。這個過程稱為內聯,需要由此表現得方法或函數稱為內聯方法或內聯函數。這個過程只是#define宏的一個更安全的版本。
在函數或方法定義中,通過將關鍵字inline置於其前,就可以指定一個內聯方法和函數。
注意:所有需要調用內聯方法或函數的源文件中,都應該提供內聯方法和函數定義。因此,如果你編寫了內聯函數或方法,就應該在頭文件中連同內聯函數或方法的原型放上其定義。對於方法,這說明,要把方法定義放在包括類定義的.h文件中,這樣做是很安全的。這就像是一個#define宏。
C++中還提供了另一種語法來聲明內聯方法,其中根本不用inline關鍵字。具體做法,就是把方法定義直接放在類定義中。
需要提到的是:首先,方法能否成為內聯方法,這有許多限制。編譯器只會內聯最簡單的方法或函數。如果定義了一個內聯方法或函數,但編譯器不想內聯此方法或函數,則可能會忽略內聯指令,但不做任何提示。其次,內聯可能導致代碼量膨脹。因此應當謹慎使用內聯。
友元函數:
C++允許類將其他類或非成員函數聲明為友元函數(friend),而且這些友元可以訪問protected和private數據成員和方法。不用在此函數定義前加上friend關鍵字。
操作符重載:
請見C++學習之四、重載C++操作符。