以前在學校學Pascal的時候,產生了一種習慣性的思維:在函數調用時,如果在函數調用前後,不希望參數的值發生改變,那麼使用值參;如果希望發生改變,那麼使用形參。
在C/C++中,不存在形參,都是值傳遞的,而引用類似於Pascal中的形參。(我不知道這樣說是否恰當)。所以,用C++的時候,我就有這種思維方式:在函數調用時,如果在函數調用前後,不希望參數的值發生改變,那麼使用值參;如果希望發生改變,那麼使用形參引用。
今天的一個偶然的錯誤,原來我的想法不對。
問題簡化,以下是一個類的定義和實現:
class CA
{
public:
CA(char *a_pcChar = "Test");
virtual ~CA();
private:
char *m_pcChar;
};
CA::CA(char *a_pcChar)
{
if(a_pcChar == NULL)
{
m_pcChar = NULL;
return;
}
m_pcChar = new char[256];
memset(m_pcChar,'/0',sizeof(char) * 256);
strcpy(m_pcChar,a_pcChar);
}
CA::~CA()
{
if(m_pcChar != NULL)
{
delete []m_pcChar;
m_pcChar = NULL;
}
}
然後再定義一個函數:
void test(CA a_clsA)
{
int i = 0;
}
當執行如下代碼:
CA clsTmpA("HelloWorld");
test(clsTmpA);
原意是:clsTmpA. m_pcChar 所指的字串仍然是“HelloWorld”,然而事與願違的是 clsTmpA. m_pcChar 所值的字串的值竟然被清掉了。
為什麼會這樣呢?
思來想去,終於弄明白了。
在執行函數test(clsTmpA)的時候,編譯器建立一個clsTmpA的副本,當然,這個副本的m_pcChar所指的地址當然與clsTmpA.m_pcChar的地址相同,此時它們所指向的字串也是相同的。
但是,當test函數執行完畢退出這個函數時,這個副本也隨之消失,因而會執行類CA的修夠函數,所以這個副本的m_pcChar指向地址被釋放。由於,副本的m_pcChar和clsTmpA.m_pcChar指向的地址相同,在退出函數時,clsTmpA.m_pcChar所指向的字串被清空也是理所當然的了。
那麼,問題怎麼解決呢!
把函數 void test(CA a_clsA)改成 void test(CA &a_clsA)就解決了。對於修改後的情況,因為參數就是個函數本身(不是副本),所以在test執行完畢,也不會調用其解構函式。
用VC試試,果真如此,OK!