彭召意 趙菁菁 劉建國
【摘 要】C++是一門經(jīng)典的程序設(shè)計(jì)語言,目前有著廣泛的應(yīng)用,學(xué)習(xí)C++語言的難點(diǎn)在于類的構(gòu)建。為了構(gòu)建好類,需要根據(jù)應(yīng)用的要求設(shè)計(jì)合理的構(gòu)造函數(shù)。文章從構(gòu)造函數(shù)的概念出發(fā),總結(jié)了其靈活多變的形式和使用方法,對(duì)想進(jìn)一步提高C++編程能力的初學(xué)者具有很好的參考價(jià)值。
【關(guān)鍵詞】C++語言;類;構(gòu)造函數(shù);教學(xué)方法
【中圖分類號(hào)】G642 【文獻(xiàn)標(biāo)識(shí)碼】A 【文章編號(hào)】1674-0688(2016)10-0050-04
C++面向?qū)ο蟪绦蛟O(shè)計(jì)是一種重要的程序設(shè)計(jì)語言,在硬件驅(qū)動(dòng)、工業(yè)控制、系統(tǒng)軟件等方面具有廣泛的應(yīng)用。不少初學(xué)者在學(xué)習(xí)C++的過程都出現(xiàn)過不少困難[1-2],其主要原因是他們沒有深刻地理解類的概念。類是面向?qū)ο蟪绦蛟O(shè)計(jì)中最重要的概念,它是構(gòu)成面向?qū)ο蟪绦虻幕T陬愔?,有一個(gè)用途廣泛的成員函數(shù),即構(gòu)造函數(shù)。設(shè)計(jì)一個(gè)類時(shí),通常都會(huì)設(shè)計(jì)構(gòu)造函數(shù)。根據(jù)應(yīng)用的不同,構(gòu)造函數(shù)會(huì)以靈活多變的形式出現(xiàn)[3-4],構(gòu)造函數(shù)在增強(qiáng)程序功能的同時(shí)也加大了初學(xué)者的難度。為了給初學(xué)者提供方便,本文就C++中的構(gòu)造函數(shù)進(jìn)行了系統(tǒng)的分析和總結(jié),同時(shí)介紹了使用方法。
1 構(gòu)造函數(shù)簡介
構(gòu)造函數(shù)是類中一種特殊的成員函數(shù),它的作用是用于對(duì)象的初始化。與其他自定義的成員函數(shù)不同,構(gòu)造函數(shù)不需要用戶來調(diào)用它,而是在建立對(duì)象時(shí)自動(dòng)執(zhí)行。
構(gòu)造函數(shù)的定義[5]:構(gòu)造函數(shù)的名字必須與類名同名,它不具有任何類型,不返回任何值。
格式如下。
構(gòu)造函數(shù)聲明:<類名> (<參數(shù)表>);
構(gòu)造函數(shù)定義如下。
(1)<函數(shù)名>(參數(shù)表)。
{//構(gòu)造函數(shù)功能體}//類內(nèi)定義函數(shù)體
(2)<類名>::<函數(shù)名>(參數(shù)表) 。
{//構(gòu)造函數(shù)功能體}//類外定義函數(shù)體
例如:定義一個(gè)包含構(gòu)造函數(shù)的汽車類Car
class Car //定義類
{
public:
Car( ) //類內(nèi)定義構(gòu)造函數(shù)
{m_strCarname = “default name”;}
private:
string m_strCarname; //數(shù)據(jù)成員
};
如果在類外定義函數(shù)體,則:
Car::Car( ) //類外定義構(gòu)造函數(shù)
{m_strCarname = “default name”;}
構(gòu)造函數(shù)的功能是由用戶根據(jù)對(duì)象初始化需要自定義和設(shè)計(jì)函數(shù)體和函數(shù)參數(shù)。
使用構(gòu)造函數(shù)的注意事項(xiàng)如下:{1}構(gòu)造函數(shù)名稱必須與類名相同;{2}構(gòu)造函數(shù)沒有返回值;{3}構(gòu)造函數(shù)由系統(tǒng)自動(dòng)調(diào)用,不需用戶調(diào)用,也不能被用戶調(diào)用;{4}在類對(duì)象進(jìn)入其作用域時(shí)調(diào)用構(gòu)造函數(shù);{5}其功能是對(duì)對(duì)象進(jìn)行初始化,一般由一系列賦值語句構(gòu)成,但是構(gòu)造函數(shù)中也可以包含其他語句,用于對(duì)象初始化時(shí)執(zhí)行的功能;{6}如果用戶自己沒有定義構(gòu)造函數(shù),則C++系統(tǒng)會(huì)自動(dòng)生成一個(gè)空的構(gòu)造函數(shù)。
根據(jù)參數(shù)的不同,構(gòu)造函數(shù)可以有不同的形式和使用方法。構(gòu)造函數(shù)的不同形式有無參構(gòu)造函數(shù)、有參構(gòu)造函數(shù)、默認(rèn)參數(shù)的構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、轉(zhuǎn)換構(gòu)造函數(shù)等。其中,拷貝構(gòu)造函數(shù)和轉(zhuǎn)換構(gòu)造函數(shù)屬于有參構(gòu)造函數(shù)。
2 普通形式的構(gòu)造函數(shù)
普通形式的構(gòu)造函數(shù)主要指帶參構(gòu)造函數(shù)(包括默認(rèn)參數(shù)的構(gòu)造函數(shù))和無參構(gòu)造函數(shù)。構(gòu)造函數(shù)可以帶參數(shù),也可以不帶參數(shù)。當(dāng)需要從外面把參數(shù)傳遞給對(duì)象時(shí),就需要采用帶參數(shù)的構(gòu)造函數(shù)。系統(tǒng)默認(rèn)的構(gòu)造函數(shù)是不帶參數(shù)的,如果想帶參數(shù),必須自定義構(gòu)造函數(shù)。
(1)帶參數(shù)構(gòu)造函數(shù)。前面例子中,寫一個(gè)帶參數(shù)的構(gòu)造函數(shù)如下。
Car::Car(string CName) //類外定義構(gòu)造函數(shù)
{m_strCarname = CName;}
帶有參數(shù)的構(gòu)造函數(shù)定義對(duì)象的格式如下:
類名 對(duì)象名(實(shí)參1,實(shí)參2,…);
無參數(shù)時(shí),定義對(duì)象的格式如下:
類名 對(duì)象名;
例如:Car c1;//建立對(duì)象c1,不帶參數(shù);Car c2(“pzy car.”);//建立對(duì)象c2,帶參數(shù)。注意:建立無參數(shù)的對(duì)象時(shí),不能帶括號(hào)。Car c1( ); //錯(cuò)誤!,不要括號(hào),否則該語句是函數(shù)聲明語句。
(2)有默認(rèn)參數(shù)構(gòu)造函數(shù)。構(gòu)造函數(shù)的參數(shù)也可以像普通函數(shù)一樣帶默認(rèn)參數(shù),帶默認(rèn)參數(shù)的構(gòu)造函數(shù)中,默認(rèn)參數(shù)值的構(gòu)造函數(shù)的一般形式如下。
類名:構(gòu)造函數(shù)名(類型 參數(shù)1=默認(rèn)值,類型 參數(shù)2=默認(rèn)值)
{函數(shù)體}
例如,前述例子中:
Car::Car(string CName=“pzy car.”)
{m_strCarname=CName;}
3 拷貝構(gòu)造函數(shù)
拷貝構(gòu)造函數(shù)能夠?qū)?shù)的屬性值拷貝給新的對(duì)象,完成新對(duì)象的初始化。它是使用類對(duì)象的引用作為參數(shù)的構(gòu)造函數(shù),也稱為復(fù)制構(gòu)造函數(shù)。
拷貝構(gòu)造函數(shù)的格式如下:
class 類名
{public:
類名(類名&變量名) //定義一個(gè)拷貝構(gòu)造函數(shù)
{ 函數(shù)體}
};
例如:定義一個(gè)帶有拷貝構(gòu)造函數(shù)的汽車類Car
class Car{
public:
Car(string con_carname, int con_seats)
{
m_strCarname = con_carname;
m_nSeats = con_seats;
}
Car(Car &con_refcar) //拷貝構(gòu)造函數(shù)
{
m_strCarname = con_refcar.m_strCarname;
m_nSeats = con_refcar.m_nSeats;
}
private:
string m_strCarname;
int m_nSeats;
};
下面3種情況會(huì)自動(dòng)調(diào)用拷貝構(gòu)造函數(shù)[6]。
(1)用早已存在的對(duì)象初始化新對(duì)象的時(shí)候。例如:Car c2=c1。
(2)將一個(gè)對(duì)象以值傳遞的方式傳給形參的時(shí)候。例如:void findcar(Carc);調(diào)用時(shí):findcar(c)。
(3)函數(shù)返回一個(gè)對(duì)象的時(shí)候。例如:return Car(c)。
拷貝構(gòu)造函數(shù)中若只完成數(shù)據(jù)成員本身的賦值,稱為“淺拷貝”。將所有數(shù)據(jù)都進(jìn)行復(fù)制的拷貝構(gòu)造函數(shù)稱之為“深拷貝”[7]。
在“淺拷貝”過程中,如果在構(gòu)造函數(shù)中有新申請(qǐng)的存儲(chǔ)空間時(shí)(比如用new操作符),由于只是完成數(shù)據(jù)本身的賦值,并沒有新申請(qǐng)空間來賦值,所以在析構(gòu)函數(shù)運(yùn)行時(shí),會(huì)出現(xiàn)錯(cuò)誤。為了避免這種錯(cuò)誤,需要采用“深拷貝”。
4 轉(zhuǎn)換構(gòu)造函數(shù)
轉(zhuǎn)換構(gòu)造函數(shù)只有一個(gè)形參,它的作用是將一個(gè)其他類型的數(shù)據(jù)(可以是基本數(shù)據(jù)類型,也可以是類對(duì)象)轉(zhuǎn)換成一個(gè)類的對(duì)象。
自定義數(shù)據(jù)類型與基本數(shù)據(jù)類型之間的轉(zhuǎn)換,除了類型轉(zhuǎn)換運(yùn)算符重載,還可以定義轉(zhuǎn)換構(gòu)造函數(shù)。所謂轉(zhuǎn)換構(gòu)造函數(shù)就是當(dāng)一個(gè)構(gòu)造函數(shù)只有一個(gè)參數(shù),而且該參數(shù)又不是本類的const引用時(shí),這種構(gòu)造函數(shù)稱為轉(zhuǎn)換構(gòu)造函數(shù)。例如:
Complex( double Real ) {real=Real;imag=0;}
其作用是將參數(shù)Real轉(zhuǎn)換成Complex類的對(duì)象(Real是double型),該對(duì)象的實(shí)部是Real,對(duì)象的虛部為0。
下面完整的例子說明了轉(zhuǎn)換構(gòu)造函數(shù)的使用。
#include
using namespace std;
class Complex
{
public:
Complex(double Real,double Imag){real= Real;imag= Imag;}
Complex( ){real=imag=0;}
Complex(double Real) //轉(zhuǎn)換構(gòu)造函數(shù)
{imag=0;real= Real;}
friend Complex operator+(Complex cp1,Complex cp2);
private:
double imag; double real;
};
Complex operator + (Complex cp1,Complex cp2)
{ Complex c;
c.real=cp1.real+cp2.real;
c.imag=cp1.imag+cp2.imag;
return c;
}
int main()
{
Complex c1(13,24),c2(5,-10),c3;
c3=c1+2.5; //用到轉(zhuǎn)換構(gòu)造函數(shù),把2.5轉(zhuǎn)換成類Complex的對(duì)象
return 0;
}
該例子是將一個(gè)標(biāo)準(zhǔn)類型的數(shù)據(jù)轉(zhuǎn)換成對(duì)象,其實(shí)參數(shù)類型也可以是其他類的對(duì)象。比如:可以將一個(gè)教師類對(duì)象轉(zhuǎn)換為學(xué)生類對(duì)象,在Student中定義如下的轉(zhuǎn)換構(gòu)造函數(shù):
Student (Teacher & t)
{num=t.num; sex=t.sex;name=t.name; }
注意:對(duì)象t中的數(shù)據(jù)成員(num,name和sex等)都要是公用成員,因?yàn)橐活愅鈦碓L問。
5 派生類的構(gòu)造函數(shù)
在定義派生類時(shí),派生類的構(gòu)造函數(shù)要考慮派生類新增的數(shù)據(jù)成員初始化,并且還要考慮基類的數(shù)據(jù)成員初始化。解決的方法是在執(zhí)行派生類的構(gòu)造函數(shù)時(shí),同時(shí)要調(diào)用基類的構(gòu)造函數(shù)。
(1)派生類構(gòu)造函數(shù)一般形式如下。
派生類構(gòu)造函數(shù)名(派生類參數(shù)表列):基類構(gòu)造函數(shù)名(基類參數(shù)表列)
{自定義派生類中的初始化語句}
(2)如果有多個(gè)基類,應(yīng)該一一把基類列出,比如有2個(gè)基類的派生類構(gòu)造函數(shù)形式如下。
派生類構(gòu)造函數(shù)名(派生類參數(shù)表列):基類1構(gòu)造函數(shù)(基類1參數(shù)表列),基類2構(gòu)造函數(shù)(基類2參數(shù)表列)
{自定義派生類中的初始化語句}
(3)派生類構(gòu)造函數(shù)的執(zhí)行順序?yàn)槭紫日{(diào)用基類的構(gòu)造函數(shù),然后執(zhí)行派生類的構(gòu)造函數(shù)體。如果是多個(gè)基類,那么調(diào)用多個(gè)基類構(gòu)造函數(shù)的順序是按照它們?cè)诼暶髋缮悤r(shí)基類出現(xiàn)的順序。
6 錯(cuò)誤使用構(gòu)造函數(shù)分析
構(gòu)造函數(shù)只能由系統(tǒng)自動(dòng)調(diào)用,不能由用戶來調(diào)用,否則將出錯(cuò)。下面用一個(gè)實(shí)例來進(jìn)行說明[8]。
#include
public:
MyBox() { m_a = 1; }
MyBox(int b)
{
m_b = b;
MyBox();
}
~MyBox() {}
void Display()
{
std::cout << m_a <<" "<}
private:
int m_a;
int m_b;
};
int main()
{
MyBox myBox(2);
myBox.Display();
return 0;
}
上述程序中,在QT環(huán)境中,輸出結(jié)果為
6422400 2
在DEV C++環(huán)境中,輸出結(jié)果為
3 2
顯然,m_a是一個(gè)不確定的值,m_b等于2,這是因?yàn)閙_a沒有被賦初值。在調(diào)用MyBox()函數(shù)時(shí),實(shí)際上是建立了臨時(shí)的MyBox類對(duì)象,MyBox()中賦值m_a=1也是對(duì)對(duì)象的賦值,因此在myBox的m_a其實(shí)并沒有被賦值。
這個(gè)例子說明了不能顯式調(diào)用構(gòu)造函數(shù),也不能給成員變量賦值,否則結(jié)果將出現(xiàn)不確定性。
7 結(jié)語
本文首先介紹了C++類的構(gòu)造函數(shù)相關(guān)概念及其靈活多變的不同形式,并指出不同的應(yīng)用場合需要建立適合需求的構(gòu)造函數(shù);然后,介紹了不同形式的構(gòu)造函數(shù)的學(xué)習(xí)和使用方法,并指出顯式調(diào)用構(gòu)造函數(shù)所帶來的后果。這些內(nèi)容有助于大家深刻理解類的構(gòu)造函數(shù),對(duì)于剛剛接觸C++語言的編程初學(xué)者和想進(jìn)一步提高C++編程能力的人員都有很大的參考價(jià)值。
參 考 文 獻(xiàn)
[1]肖菁.高校非計(jì)算機(jī)專業(yè)C/C++教學(xué)的探索與實(shí)踐[J].現(xiàn)代計(jì)算機(jī):專業(yè)版,2011(30):21-22.
[2]魯紅英,肖思和,孫淑霞.C/C++語言程序設(shè)計(jì)課程教學(xué)改革與實(shí)踐[J].計(jì)算機(jī)教育,2013(7):95-98.
[3]王帥,馬夢娜.關(guān)于C++構(gòu)造函數(shù)的幾點(diǎn)探究[J].電腦編程技巧與維護(hù),2013(10):6-7.
[4]李欣然,靳雁霞.C++程序設(shè)計(jì)中構(gòu)造函數(shù)的探討[J].計(jì)算機(jī)時(shí)代,2011(12):30-32.
[5]譚浩強(qiáng).C++面向?qū)ο蟪绦蛟O(shè)計(jì)[M].北京:清華大學(xué)出版社,2006.
[6]百度百科.構(gòu)造函數(shù)[EB/OL].http://baike.baidu.com/link?url=uhoOfoj3mULwrmajVpFgRYwfomllKB-1VuO-bbCHnPikWcMMKFOAUqDCWlnTlokl2MH3psipethK-HyAxSfu8qlIv3bPfFOY4gChQ7CGe3KWo4kG1Va-yW-ZEEOQ9GHP1-a,2016-10-03.
[7]傳智播客高教產(chǎn)品研發(fā)部.C++程序設(shè)計(jì)教程[M].北京:人民郵電出版社,2015.
[8]Ticktick.顯式調(diào)用構(gòu)造函數(shù)產(chǎn)生的悲劇[EB/OL].http://ticktick.blog.51cto.com/823160/294573,2016-10-03.
[責(zé)任編輯:陳澤琦]