之前工作中遇到一個問題,就像題目中描述的那樣,看起來題目有些拗口復雜,這里解釋下,當時遇到的需求需要這樣處理:調用某個類對象的某個成員函數時,第一次有具體意義的,其他時候都是保持不變的、無意義的。這個需求可以看做是在調用某成員函數時,第一次進行初始化,其他時候不進行操作,即在首次調用時進行初始化,根據這點,很容易想到c/c++里面的static變量,它的作用是保持變量內容的持久,存儲在靜態數據區的變量會在程序剛開始運行時就完成初始化,也是唯一的一次初始化。根據需求,使用static局部變量,寫下如下代碼:
1 class T 2 { 3 public: 4 T(const char * pstr):value(pstr){} 5 void Print()const; 6 private: 7 string value; 8 }; 9
10 void T::Print()const
11 { 12 static bool bFirstCall = true; 13 if(bFirstCall) 14 { 15 cout<<"first Call "<<value<<endl; 16 bFirstCall = false; 17 } 18 else
19 { 20 cout<<"not first Call "<<value<<endl; 21 } 22 }
即在類的成員函數中定義一個局部的靜態變量,使得第一次調用進行初始化,用以區分是否是第一次調用,之后運行else分支。運行下面的測試代碼,輸出結果見下圖:
1 int _tmain(int argc, _TCHAR* argv[]) 2 { 3 T t1("Grubby"); 4 t1.Print(); 5 t1.Print(); 6
7 T t2("Moon"); 8 t2.Print(); 9 t2.Print(); 10
11 return 0; 12 }
通過輸出可以看到,t1得到了想要的結果,但是兩次t2.Print()都打印 "not first Moon",這說明此時 bFirstCall 還是false。於是在Print函數中打印bFirstCall地址的代碼:
1 void T::Print()const
2 { 3 static bool bFirstCall = true; 4 printf("addr of bFirstCall is %x\n", &bFirstCall); 5 if(bFirstCall) 6 { 7 cout<<"first Call "<<value<<endl; 8 bFirstCall = false; 9 } 10 else
11 { 12 cout<<"not first Call "<<value<<endl; 13 } 14 }
再次運行輸出如下:
看到四次調用Print函數時打印的bFirstCall的地址相同,這說明類成員函數中的局部靜態變量同樣屬於此函數,而不屬於某個對象,不會因為重新定義一個t2對象,第一次調用 t2.Print()時 bFristCall 是 true,不管是哪個T類對象,只在第一次調用 Print()時,bFirstCall == ture。於是無奈對程序進行了修改,將bFristCall定義為類的靜態成員,每次構造函數是將其重置為true:
1 class T 2 { 3 public: 4 T(const char * pstr):value(pstr){ bFirstCall = true;} 5 void Print()const; 6 private: 7 string value; 8 static bool bFirstCall; 9 }; 10 bool T::bFirstCall; 11
12 void T::Print()const
13 { 14 if(bFirstCall) 15 { 16 cout<<"first Call "<<value<<endl; 17 bFirstCall = false; 18 } 19 else
20 { 21 cout<<"not first Call "<<value<<endl; 22 } 23 }
運行測試代碼可以得到想要的結果:
但是這里又出現了新的問題,即每次構造函數是將bFirstCall重置為true時,如果新定義一個對象,並且沒有調用Print()函數,那么再次調用之前定義對象的Print()函數,會產生與預期相反的結果,考慮如下測試代碼:
1 int _tmain(int argc, _TCHAR* argv[]) 2 { 3 T t1("Grubby"); 4 t1.Print(); 5 t1.Print(); 6
7 T t2("Moon"); 8 t1.Print(); 9
10 return 0; 11 }
運行輸出如下:
避免這種情況,目前只能保證順序的對每個對象進行類似Print這樣函數的初始化調用,還沒有想到好的解決辦法。
