程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Effective C++ 45-48

Effective C++ 45-48

編輯:C++入門知識

Effective C++ 45-48


45。弄清c++在幕後為你所寫,所調用的函數。

如果設置一個空類,c++編譯器會聲明以下函數:拷貝構造函數,賦值運算符,析構函數,一對析構函數(const和非const)。而如果你沒有聲明任何構造函數的話,編譯器會為你聲明一個缺省構造函數。這些函數都是公有的。

編譯器生成的缺省構造函數和析構函數實際上什麼也不做,生成的析構函數一般是非虛構的,除非繼承了一個具有虛析構函數的基類。缺省取地址符只是返回對象的地址,即return this。而拷貝構造函數和賦值運算符,對類的非靜態數據成員進行“以成員為單位”逐一拷貝構造或賦值,也就是淺拷貝。

當類中有引用時,默認的拷貝函數無法實現,編譯器會報錯,有常量也是,有指針是,會發生淺拷貝但是運行上沒有錯誤。對於含有指針,引用和const成員的類需要自己定義賦值運算符和復制構造函數。而如果將派生類中的賦值運算符或拷貝構造函數聲明為private,編譯器也會拒絕為這個派生類生成相應的賦值運算符和拷貝構造函數。


46.寧可編譯和鏈接時出錯,也不要在運行時出錯。

當通過編譯和鏈接後,只有極少數情況會讓C++拋出異常,如內存耗盡,運行時錯誤和C++沒什麼關系。C++沒有運行時檢測,要盡量避免運行時錯誤。

對於運行時錯誤,在一個運行中沒有錯誤,並不表示其就是正確的了,因為每次程序運行的狀態都不一樣。

而避免運行時錯誤的一般方法是對設計做一些小小的改動,就可以在編譯期間消除可能產生的運行時錯誤。一般設計在程序中增加新的數據類型,以在編譯時檢測數據的安全性。

對於一個日期類,有構造函數:Date(int day,int month,int year);實現這個類面臨的問題是對day和month進行合法性檢測,如果不進行檢測,由於其內部邏輯可能會導致一些運行時錯誤。一種簡單的方法是使用枚舉

enum Month {Jan = 1,Feb = 2,....,Dec =12};
而構造函數改為:

Date(int day,Month month,int year);
但是這樣做沒有多大好處,因為枚舉類型不用初始化,即直接 Date d(1,Month m,2014),能通過編譯,但是運行時出錯。

即想免除運行時檢查,又要保證足夠的安全性,選擇使用一個類來實現month。

class Month{
public:
	static const Month Jan(){return 1;}//這裡其實是調用隱式構造函數,其實返回值為 Month(1);
	//....
	static const Month Dec(){return 12;}//使用靜態函數,返回一個常量,防止隨意改動
	int toInt() const 
	{return number;}
private:
	Month (int n):number(n){}
	const int number;
};
這裡調用類的靜態成員返回對應的Month,而構造函數隱藏,防止用戶自己去創建新的month。但即使有了這樣的類,用戶還是可以指定一個非法的month,如下:

	Month* m;
	Data(1, *m ,2014);
消除所有的運行時檢測是不切實際的。但將檢查由運行時轉移到編譯或鏈接時一直值得努力的目標,這樣做,會使程序更小,更快,更可靠。


47.確保非局部靜態對象在使用前被初始化。

使用對象前一定要初始化。

非局部靜態對象是指 : 定義在全局或名字命名空間內,或在一個類中被聲明為static,或在一個文件范圍內被定義為static。就是值全部的對象,去掉非靜態 的局部變量 和函數內的靜態變量。

當類依賴與這些非局部靜態對象時,如在 一個文件中有一個全局對象theCountry, 在另外一個文件中有一個對象theCity,對city的初始化依賴與country的初始化。而程序的正確運行依賴於它們的初始化順序。但確定非局部靜態對象初始化的正確順序很困難,在多個編譯單元中確保每個這樣的對象初始化是很困難的,尤其當程序變得更加復雜增加更多的這種非局部靜態對象的情況下。

單一模式,將每個非局部靜態對象轉移到函數中,聲明其為static,其次,讓函數返回這個對象的引用。這樣用戶就可以通過函數調用來指明對象,即用函數內部的static對象來取代非局部靜態對象。因為對於函數的靜態對象什麼時候被初始化,c++明確的指出了。這樣的另一個好處是如果這個模擬非局部靜態對象從沒被調用,也就永遠沒有對象構造和銷毀的開銷。簡單的例子:

class country{....};
country& theCountry(){
	static country tc;//定義和初始化theCountry
	return tc;//返回它的引用。
}

48.重視編譯器警告。

一般程序員都會忽略編譯器警告,畢竟沒有出錯。要理解編譯器的各種警告的含義。書上舉個例子:

class A{
public:
	virtual void f() const{cout<<"fA";}
};
class B:public A{
public:
	virtual void f(){cout<<"fB";}
};
書上說有編譯器在這裡會出一個 B::f() hides virtual A:::f()的錯誤,即A中聲明的f函數並沒有在B中重新定義,但是被B中新聲明的非const的f函數給隱藏了。但是這樣沒有實現多態,對於使用聲明為A的指針的B的對象指向f函數的話,會調用A中的f函數。但是我用的vs2012中並沒有提示這條警告。


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved