時間:2014.04.04
地點:基地二樓
--------------------------------------------------------------------
和類的靜態數據成員一樣,有時整個類也只要一份即可。這個方法應該屬於類,而不轉屬於某個對象。這些方法不會訪問特定對象的信息。比如:
class MyClass { //...... protected: static string DoubleToString(double val); static double StringToDouble(const string& str); //...... };但有一個問題,類的常量數據成員常設置為靜態的,但這裡的靜態方法不允許聲明為const方法。而且方法的實現無需重復static關鍵字了。靜態方法因為不屬於特定對象,於是沒有this指針,當用某個特定對象調用靜態方法時,靜態方法不會訪問這個對象的非靜態數據成員。實際上靜態方法就像一個普通函數,區別只在於這個方法還可以訪問類的private和protected靜態數據成員。類中的任何方法都可以像調用普通函數那樣調用類的靜態方法。如果想在類外調用靜態方法,加上作用域解析運算符限定即可。總的一句說來,靜態方法更像一個普通函數,只是屬於這個類而已再加上一點作用域修飾符限制。
--------------------------------------------------------------------
常量對象時值不能改變的對象,即如果有一個對象是常量對象,我們不能試圖去通過一些操作來改變裡面數據成員的值。這樣在使用常量對象,包括常量對象的引用和常量對象的指針,編譯器都不允許調用對象的一般方法,除非該方法能保證不會改變任何數據成員,那麼方法是如何承諾不會改變對象的數據的呢?它得簽署協議,發表聲明,說自己是const的才行,即用const關鍵字標記自己。比如:
class MyClass { public: //...... double get_value() const; string get_string() const; //...... }這裡的const和剛剛上面的static就不一樣了,const屬於方法原型的一部分,方法的定義必須與之匹配
double MyClass::get_value() const { return value_; } string MyClass::get_string() const { return string_; }將方法標記為const意味著方法發誓承諾不會在方法內改變對象內部的值。那麼前面說的靜態方法為什麼不能聲明為const呢?這顯然是多余的,靜態方法沒有類的實例,都不可能接觸到類的內部數據成員,不可能改變內部值,const的機制時將方法內用到數據成員的標記為const,表示發誓此生不能被利用來修改數據成員,只能查看和訪問。否則編譯不通過。
要說明的是非const對象是可以調用const方法和非const方法的,只是const對象只能調用const方法。要形成這樣一種習慣,將不修改對象的所有方法都聲明為const。但const對象也會被銷毀,析構函數會被調用,我們不應該將析構函數聲明為cosnt。
還有一個問題,有時你編寫的代碼邏輯上是const的,要碰巧我們需要改變對象的數據成語,這個邏輯上const的只是邏輯上的,是假的,我們想在這裡const裡改變數據成員確定是可控的,不會帶來任何負面影響,而且恰巧是完成任務所必須的。例如,假設你想獲得類中獲取數據被讀取的頻率,我們可以在類中設置一個計數器,每次調用get函數時對計數器加1,一方面我們確實要讓get函數是const的,因為並不想通過它來修改正式的類數據成員,另一方面,我們確實想改變類的一個數據成員,盡管它聽起來沒那麼正式。一種解決這種矛盾的辦法就是將變量設置為mutable。告訴編譯器在const方法中是允許改變的。
class MyClass { //...... protected: double value_; string string_; mutable int num_access_=0; };
double MyClass::get_value() const { num_access++; return value_; } string MyClass::get_string() const { num_access++; return string_; }
--------------------------------------------------------------------
類中可以編寫多個構造函數,這些構造函數名稱相同,只是參數數量或類型不同。C++中對於其他方法也一樣,這叫做方法重載或者函數重載,編譯器會根據傳遞的參數的不同判斷具體調用哪個實例,這一步叫做重載解析。但注意C++中不允許根據方法的返回類型重載方法名稱,而根據const重載方法時可以的。在存在const 和非const兩個版本的類中,const對家將調用const版本,因為對於const對象而言,非const方法本身就是不可見的,而對於非const變量而言,原則上兩個版本都可調用,但在存在非const版本的情況下,優先選擇調用非const版本。
C++11中重載方法可被顯示刪除,防止調用具有特定參數的成員函數,比如:
class MyClass { public: void foo(int i); };我們可以這樣調用都正確
MyClsss c; c.foo(123); c.foo(1.23);編譯器會將double值1.23轉換為整型值1,然後調用foo(int I)。也許這樣做並不是我們所期待的,於是我們要顯示刪除foo()的double實例,禁止編譯器這麼轉換。原理是:void foo(double i) 顯示的有,碰到1.23時編譯器會去找這個函數,而不發生隱式轉換去找foo(int i)了,但foo(double i)是delete的,所以不會執行。實現如下:
class MyClass { public: void foo(int i); void foo(double d)=delete;
--------------------------------------------------------------------
C++中函數原型或方法可指定默認值,要是用戶指定了這些參數,默認值被忽略,否則將會使用默認值,限制是默認值的給出只能從最後邊的參數開始提供連續的默認參數列表,否則編譯器無法用默認值匹配遺失的參數。默認參數在構造函數中最有用。例:
class Spreadsheet { public: Spreadsheet(const SpreadsheetApplication& the_app,int in_width=kMaxWidth,int in_height=kMaxHeight); };所有參數都有默認值得構造函數等用於默認構造函數,即可在創建類對象時不指定任何參數。如果試圖同時聲明默認構造函數和所有參數都有默認值得構造函數編譯器會報錯。因為二者的等效性,當不提供任何參數數,編譯器不曉得調用哪個構造函數了。
--------------------------------------------------------------------
C++中提供這樣一種能力,函數或者方法的調用可以不以生成代碼的方法實現,就像調用獨立的代碼塊,編譯器會將方法體或者函數體直接插入到調用方法或者函數的位置,俗稱函數體替換,就像我們的宏函數一樣。這個過程成為內聯。具有這一行為的函數或方法成為內聯方法或函數。內聯比使用#define宏要安全。具體實現是在方法或者函數定義前使用inline關鍵字將它們指定為內聯的。例如:
inline double MyClass::get_value() const { num_accesses++; return value; } inline string MyClass::get_string() const { num_accesses++; return string_; }這樣編譯器就可選擇用實際的方法體替換get_value以及get_string的調用,而不是生成代碼通過函數調用。
值得注意的問題是:如果沒有看到函數的定義,編譯器怎麼替換函數體?因此編寫內聯函數或者方法時,應該將定義與原型一起放在頭文件中。
C++提供另外一種聲明內聯方法的語法,而無需使用inline關鍵字,而是直接將方法定義放在類的定義中。比如:
class MyClass() { public: //...... double get_value() const{ num_access++;return value_;} string get_string() const{ num_access++;return string_;} //...... };內聯方法的應用受限,編譯器只會內聯最簡單的方法以及函數,如果將編譯器不想內聯的方法定義為內聯,編譯器會自動忽略,其次,內聯使得代碼膨脹,在每個調用的地方都會重新生成方法體,這會增加可執行程序的大小。