本文以C為元語言,C++為對象語言,以C++類串中的構(gòu)造函數(shù)、析構(gòu)函數(shù)、賦值函數(shù)和成員轉(zhuǎn)換函數(shù)等概念為例,用C程序描述它們的產(chǎn)生過程,進而用C程序描述C++程序。
1構(gòu)造函數(shù)和析構(gòu)函數(shù)
(1) 從C的構(gòu)造函數(shù)SetString到C++的轉(zhuǎn)換缺省構(gòu)造函數(shù)的過程是,將指針參量s改名為this后隱藏,給參數(shù)c加上缺省值,去掉返回值類型說明。[2]
構(gòu)造函數(shù)實質(zhì)上是初始化語言的推廣,經(jīng)過C描述的結(jié)構(gòu),到C++階段達到了和語言內(nèi)部類型的初始化語句統(tǒng)一的形式。例如:
String S(\"ok\");
等價于
String S=\"ok\";
和語言內(nèi)部類型變量的初始化語句的形式統(tǒng)一,例如:
float f=5;
給用戶定義的類的對象初始化,一般不僅需要傳遞初始值,還要定義初始化過程,特別是當這個類含有指針成員的時候,所以構(gòu)造函數(shù)保留了參數(shù)表和函數(shù)體。之所以去掉返回值類型說明,是因為初始化過程是在構(gòu)造函數(shù)體內(nèi)完成的,不是由構(gòu)造函數(shù)的返回值完成的。
語言內(nèi)部類型變量還有復(fù)制初始化語句:
float f=5;
float g=f;
所以用戶類型,特別是含有指針成員的類型,需要增加復(fù)制構(gòu)造函數(shù):
String::String(const String cs); //復(fù)制構(gòu)造函數(shù)
應(yīng)用舉例:
String S=\"ok\";
String T(S);
等價于
String T=S;
相應(yīng)的,語言內(nèi)部類型也增加了等價的初始化語句形式:
float f(5);//float f= 5;
float g(f);//float g=f;
復(fù)制構(gòu)造函數(shù)調(diào)用語句所具有的初始化賦值形式?jīng)Q定了其參數(shù)應(yīng)該是引用型。[2]
(2) 對語言內(nèi)置類型之間的轉(zhuǎn)換,編譯器自有轉(zhuǎn)換機制,依據(jù)“升格”原則自動轉(zhuǎn)換。例如實行下面的語句
float f=5;
就是自動轉(zhuǎn)換,把整數(shù)5轉(zhuǎn)換為單浮點型賦給f。
但是用戶類型之間的轉(zhuǎn)換,用戶類型和內(nèi)置類型之間的轉(zhuǎn)換,需要用戶定義轉(zhuǎn)換函數(shù)。編譯器遇到非內(nèi)置類型之間的轉(zhuǎn)換,就會去尋找相應(yīng)的轉(zhuǎn)換函數(shù)。當構(gòu)造函數(shù)僅有一個參數(shù),或僅有第一個參數(shù)沒有默認值,而且這個參數(shù)是不同于該類的另一種數(shù)據(jù)類型,這個構(gòu)造函數(shù)就叫轉(zhuǎn)換構(gòu)造函數(shù)。因此,串類的缺省構(gòu)造函數(shù)
String(const char *c=\"\");
也是轉(zhuǎn)換構(gòu)造函數(shù)。實行下面的語句
String S=\"ok\";//String S(\"ok\");
就需要隱式調(diào)用轉(zhuǎn)換構(gòu)造函數(shù),我們稱之為隱式轉(zhuǎn)換。
這種隱式轉(zhuǎn)換經(jīng)常發(fā)生在函數(shù)調(diào)用中,如表2所示。
也有這樣的時候,一個類不允許隱式調(diào)用轉(zhuǎn)換構(gòu)造函數(shù),這就要在轉(zhuǎn)換構(gòu)造函數(shù)聲明前加關(guān)鍵字explicit,即顯式調(diào)用轉(zhuǎn)換構(gòu)造函數(shù):
String S=String(\"ok\"); //顯式調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)
或
String S=(String) \"ok\"; //顯式調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)
表2中的調(diào)用語句
func1(\"ok\");
要改進為:
func1(String(\"ok\"));//或func1((String) \"ok\");
以前我們是這樣解釋return語句的:假設(shè)函數(shù)返回值類型是類串,那么
return(\"ok\");
相當于
String _temp=\"ok\";//系統(tǒng)根據(jù)返回值類型創(chuàng)建一個臨時變量
但是有了explicit修飾符,這樣的解釋就不夠了。這需要隱式類型轉(zhuǎn)換,而如果串類的轉(zhuǎn)換構(gòu)造函數(shù)有explicit修飾,這就是非法的。因此對含有用戶類型的調(diào)用,需要重新解釋return的內(nèi)部實現(xiàn)過程如下:
String _temp=String(\"ok\");//顯式調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)
類似的形式還有:
String T=String(S);//顯式調(diào)用拷貝構(gòu)造函數(shù)
相應(yīng)的,對語言固有類型也需要引入相應(yīng)的初始化形式,以達到統(tǒng)一。例如
float _temp=int(5);
float g=float(f);
(3) 在C++中,析構(gòu)函數(shù)是構(gòu)造函數(shù)的逆運算,名稱是構(gòu)造函數(shù)名前加“~”,如~String。結(jié)束生命周期時,系統(tǒng)自動調(diào)用析構(gòu)函數(shù)。一個類,若含指針成員,且指向動態(tài)空間,則必須創(chuàng)建析構(gòu)函數(shù)。
運算符new在隱含調(diào)用構(gòu)造函數(shù)時需要傳遞的參數(shù)在其后加括號給出。
String *pS;
pS=new String(\"ok\");
運算符delete與free的主要區(qū)別是,前者在釋放pS指向的空間之前,負責調(diào)用析構(gòu)函數(shù)。
2成員賦值運算符函數(shù)
一個C結(jié)構(gòu)串或C串,不能通過賦值運算符“=”,只能通過賦值函數(shù)給另一個結(jié)構(gòu)串賦值,原因是結(jié)構(gòu)串含有指針成員。現(xiàn)在C++允許把成員賦值函數(shù)擴展為成員賦值運算符函數(shù)operator=,使賦值函數(shù)形式與賦值運算符形式等價。
C++串類的應(yīng)用舉例:
String S,T,Y,W;
W=\"work\";//W.operator=(\"work\");
Y=W;//Y.operator=(W);
S=T=Y;//S.operator=(T.operator=(Y));
(S=T)=Y;//(S.operator=(T)).operator=(Y);
(S=\"hard\")=Y;//(S.operator=(\"hard\")).operator=(Y);
對應(yīng)的C串結(jié)構(gòu)應(yīng)用舉例:
String S,T,Y,W;
SetString(S,\"\"), SetString(T,\"\"), SetString(Y,\"\"), SetString(W,\"\");
CStrAssign(S,\"work\");
StrAssign(Y,W);
StrAssign(S,StrAssign(T,Y);
StrAssign(StrAssign(S,T),Y);
StrAssign(CStrAssign(S,\"hard\"),Y);
(1) 復(fù)制賦值運算符函數(shù)和轉(zhuǎn)換賦值運算符函數(shù)有區(qū)別:前者是用一個類串修改另一個類串;后者是用一個C串修改一個類串:
String S,T,W (\"work\");
S=W;//調(diào)用拷貝賦值運算符函數(shù)
T=\"work\";//調(diào)用轉(zhuǎn)換賦值運算符函數(shù)
(2) 賦值運算符函數(shù)與構(gòu)造函數(shù)有區(qū)別:前者是用一個已存在的串修改另一個已存在的串,而且有返回值;后者是用一個已存在的串創(chuàng)建一個串:
String T=\"study\", W=\"work\";//調(diào)用缺省構(gòu)造函數(shù),用C串創(chuàng)建類串
String S=W;//調(diào)用拷貝構(gòu)造函數(shù),用類串創(chuàng)建類串
T=W;//調(diào)用拷貝賦值運算符函數(shù),用類串修改類串
T=\"work\";//調(diào)用轉(zhuǎn)換賦值運算符函數(shù),用C串修改類串
(3) 如果沒有轉(zhuǎn)換賦值運算符函數(shù),那么C++編譯器會通過轉(zhuǎn)換構(gòu)造函數(shù)和拷貝賦值運算符函數(shù)聯(lián)合實現(xiàn)其功能:先調(diào)用轉(zhuǎn)換構(gòu)造函數(shù),生成臨時匿名對象,再調(diào)用拷貝賦值運算符函數(shù),將臨時對象賦值。例如:
T=\"work\";
C++編譯器將其轉(zhuǎn)換為:
T.operator=String(\"work\");
即
String _temp=\"work\";//調(diào)用轉(zhuǎn)換構(gòu)造函數(shù),生成臨時匿名對象,假設(shè)為temp
T=_temp;//調(diào)用拷貝賦值運算符函數(shù),用臨時對象賦值
概括起來:
轉(zhuǎn)換賦值運算符函數(shù)=轉(zhuǎn)換構(gòu)造函數(shù)+賦制賦值運算符函數(shù)
3成員轉(zhuǎn)換函數(shù)
與轉(zhuǎn)換構(gòu)造函數(shù)和轉(zhuǎn)換賦值函數(shù)的轉(zhuǎn)換方向相反,C++成員轉(zhuǎn)換函數(shù)把類對象轉(zhuǎn)換為他類對象(見表5)。
應(yīng)用舉例:
String S(\"work\"),T(\"hard\");
char *c1=S;//char* c1=S.operator char*();
char *c2=(char*)S;//char* c2=S.operator char*();
c1=T; //c1=T.operator char*();
c2=(char*)T;//c2=T.operator char*();
4結(jié)束語
把C作為C++的元語言,這時C++的概念就可以用C程序嚴格地描述,避免C++解釋中的歧義性和模糊性,有利于理解;而且對C++,我們就不必從開頭講起,可以直接講授C++相對C的修正和補充部分,有利于深入。另外,C++的新標準包含數(shù)據(jù)結(jié)構(gòu),說明C++和數(shù)據(jù)結(jié)構(gòu)是一個整體,而C語言作為元語言,其教學(xué)內(nèi)容也應(yīng)該包含數(shù)據(jù)結(jié)構(gòu),但是包含多少為宜,根據(jù)Occam原理“每一個公理系統(tǒng)都是可以改進的,但改進不能超過其必要性。”[1]還有許多需要我們大家共同研究的地方。
參考文獻:
[1] 李未. 數(shù)理邏輯[M]. 北京:科學(xué)出版社,2008. 21.
[2] 王立柱. C/C++與數(shù)據(jù)結(jié)構(gòu)(第3版)(上冊)[M]. 北京:清華大學(xué)出版社, 2008.221,214.