1.1. void類型
void類型其實是一種用於語法性的類型,而不是數據類型,主要用於作為函數的參數或返回值,或者定義void指針,表示一種未知類型。
1.1.1. 作為函數參數與返回值
void func( void );
void func( );
例如上面兩例,其實兩種聲明方式是等效的,在C++中如果參數列表為空,默認的參數類型即為void,但建議沒有參數時使用void以提高程序的可讀性。
因 為C++在定義函數時不允許返回值類型為空,在C++98之前,是允許定義函數時不定義返回值的,默認的返回值是int類型。其實默認int類型並不是好 事,如果函數有返回值在函數返回時是需要消耗CPU傳遞返回值的,也或許也是C++98標准將默認返回值類型改為void的原因。
因為C++不允許默認返回值,所以當函數不需要返回值是,需要將返回值類型聲明為int類型。當調用返回值類型為void類型的函數時,在工程上有很多實際代碼在前面加上(void)類型轉換,以提高代碼的可讀性。如調用上面定義的 func函數。
(void)func( );
從另一個角度講,這樣嚴謹的方式是可以提高軟件的健壯性的,調用函數時可以明確地看出是沒有返回值的,如果調用一個返回值不是int類型的函數時最好判斷其返回值,以檢查函數調用是否成功,如:
#include
char buff[5];
func( );
snprintf(buff, sizeof(buff), “%d”, “10240”);
顯 然這段代碼是有問題的,當然func沒有返回值,這樣調用是沒有問題,但snprintf的調用會有問題因為緩沖區有可能太小而不能容納結果字符串,上面 的代碼就有這個問題。假設我們不知道snprintf有沒有返回值,可能這個BUG我們不會發現,直到有一天出現了我們不期望的結果。如果我們嚴格要求調 用每個函數時必須判斷函數的返回值,按照以下面的代碼編碼,就不會出給我們的程序造成隱患。
#include
#include
char buff[5];
(void)func( );
if( sizeof(buff) <= snprintf(buff, sizeof(buff), "%d", 10240) ){
buff[sizeof(buff) - 1] = '/0';
throw std::overflow_error("buff overflow");
}
1.1.2. void指針
void* pv = NULL;
string str = "string";
int i = 1;
pv = &str;
pv = &i;
int *pi = (int*)pv;
string* ps = (string*)pv;
如 上面的示例所示,void指針表示未知類型的指針,可以將任意類型的指針直接賦值給void指針,好比是C#、Java中的object引用,可以把任意 類型的對象賦值給它。但把void類型賦值給特定類型的指針時,就需要進行強制轉換,因為C++為類型語言,盡可能保證類型安全,如果使用了強制類型轉 換,編譯器就認為程序員知道他(她)在干什么,程序也應該負起這樣做的責任。
值得注意的是,函數指針與類成員的指針不能賦值給void*類型變量。
void* 在C語言中一般用於動態內存的操作,因為malloc和calloc返回的類型都是void*類型。在W3C的協議庫libwww里,大量使用了 void*類型,如果使用C++的繼承特性的話,應該會使代碼可讀性更好。而在C++中則可以使用new返回特定類型指針,更不容易出現問題,所以 void*的作用顯得更弱了。
void*在C++中的主要作用就是作為函數指針的返回值[C++ Programming Language],例如:
void* my_alloc(size_t size);
void* 還用於一些底層的操作,例如我們有兩個類UdpSocket和TcpSocket,在我們一個傳輸類中需要支持兩種協議,提供統一的接口,但 UdpSocket和TcpSocket之間沒有繼承關系,無法使用共同的基類指針,而只能使用void*指針,代碼如[??]。
class UdpSocket{};
class TcpSocket{};
class Transfer
{
public:
enum Protocol { UDP, TCP };
Transfer(Protocol prot) : _prot(prot)
{
if( _prot == UDP)
_sock = new UdpSocket( );
else if( _prot == TCP )
_sock = new TcpSocket( );
else
throw std::invalid_argument("prot");
}
~Transfer( void )
{
if(_prot == UDP)
delete (UdpSocket*)_sock;
else
delete (TcpSocket*)_sock;
_sock = NULL;
}
private:
void* _sock;
Protocol _prot;
};