Q: 有人和我說如果我要對一個變數做增加(increment)的動作 ,我應該使用
++x 而不是使用
x++,這是真的嗎?
A: 如果你要做的事就只是增加 (沒有要把這個變數給(assign)別人),那麼這是個好習慣,特別是你在一個class上使用increment operator時。
// Prefix operators are preferred in cases in the following common cases:
for (;checkstate(x);++x) dosomething(x);
++x;
標準的資料型態其實是不會有效能上的差異,但是對於 class 的確是有的。原因是(大多數的實作上),postfix operator會對原本的變數做一個暫時的複製,而回傳值是以 by value 的方式回傳,不是 by reference,下面的這個例子可以說明一切:
class MyClass
{
public:
MyClass& operator++() //Prefix increment operator (++x)
{
//Perform increment operation
return *this;
}
MyClass operator++(int) //Postfix increment operator (x++)
{
MyClass temp = *this;
//Perform increment operation
return temp;
}
};
Q:如何overload postfix的++以及--運算子? (像是x++, x--) ?
A: 必須以一個
int 做為參數:
class MyClass
{
public:
MyClass& operator++(); //Prefix increment operator (++x)
MyClass operator++(int); //Postfix increment operator (x++)
MyClass& operator--(); //Prefix decrement operator (--x)
MyClass operator--(int); //Postfix decrement operator (x--)
};
Q: 參數是如何傳遞到function的?
A: 在C++當中有二種方式,一種是by value,一種是by reference。By value表示被呼叫者的stack會產生一份原參數的複製品,任何在此複製品上的動作都不會影響到原本function的參數。而by reference代表東西的位置會被傳遞(reference握有某個東西位置,但它表現得像是它自己就是那東西),所以被呼叫者可以直接更動最原始被傳進來的那個東西。
Q: 那麼所謂的"pass by pointer"是什麼?
A: 沒有這種東西,這是一種錯誤的概念。一個指標就是握有某個記憶體位置。你可以傳遞一個指標變數到一個function中,但這個指標變數是"pass by value"的。你可以簡單的說,如果一個function prototype的參數是有'&'的,那麼它就是pass by reference,否則就是pass by value。
Q: 我可以看些例子嗎?
A: 可以,下面是幾個例子:
第一個例子是一個整數由pass by value傳遞到function foo()中,並且將它們變大。由於一份複製品會產生,所以變大不會影響原本的變數,所以程式會輸出0 1 0。
void foo(int i)
{
i++;
cout << i << endl;
}
int main()
{
int i = 0;
cout << i << endl;
foo(i);
cout << i << endl;
return 0;
}
由於pass by value需要建立一個複製品,所以當你在傳遞東西時,如果它是個class或是struct的話,它們必須提供copy constructor才行,因為這樣才能讓function去做複製品。
下面這個例子在foo() function中使用了pass by reference,它可以真的去改變原始的參數,所以它會輸出 0 1 1 。
void foo(int &i)
{
i++;
cout << i << endl;
}
int main()
{
int i = 0;
cout << i << endl;
foo(i);
cout << i << endl;
return 0;
}
你也可以將一個指向整數的指標(即一個位置)傳遞到foo()中。指標的傳遞是pass by value (這代表如果你在foo()當中改變pointer的話不會影響在main當中的pointer值),但是由於這複製品亦是main當中數值的位置,所以我們可以直接改變該數。這個程式也會輸出0 1 1。
void foo(int *i)
{
(*i)++;
cout << *i << endl;
}
int main()
{
int i = 0;
cout << i << endl;
foo(&i);
cout << i << endl;
return 0;
}
當function以指標為參數時,有可能發生別人將NULL pointer傳進來的情況(各種理由都有可能),所以你最好逼免掉這樣的bug。
假設,你想要傳遞一個指標某個buffer的指標,並且希望該function(重新)為它配置記憶體空間,並且將此buffer填滿,就像這樣:
// 這程式是"錯的"
void foo(byte *buffer)
{
// delete the previous buffer
if(buffer != NULL)
delete [] buffer;
// allocate new memory
buffer = new byte[2];
// fill the memory
buffer[0] = 0;
buffer[1] = 1;
}
int main()
{
byte * buffer = NULL;
foo(buffer);
delete [] buffer;
return 0;
}
這將會造成錯誤,就如同前面所解釋的,由於傳遞指標是pass by value,一份指標複製品會產生,當function return回到main之後,main當中的指標仍舊會是指向原本的位置,和呼叫foo()之前一樣 (在這個例子中就是NULL)。要做到你想做的事,你必須在傳遞指標時 (即buffer的起始位置),使用pass by referene。
// 這是修正後正確的程式
void foo(byte* &buffer)
{
// delete the previous buffer
if(buffer != NULL)
delete [] buffer;
// allocate new memory
buffer = new byte[2];
// fill the memory
buffer[0] = 0;
buffer[1] = 1;
}
int main()
{
byte * buffer = NULL;
foo(buffer);
delete [] buffer;
return 0;
}
這可以保證在foo()中更改指向byte陣列的指標,可以真的更動到main當中的指標值。