一、構造函數的作用
構造函數是一種特殊的成員函數,與其他成員函數不同,不需要使用者來調用它,而是在建立對象時自動執行。
注意:
* 構造函數的名字必須與類名同名,而不能由使用者任意命名,以便編譯系統能識别它并把它作為構造函數處理。
*它不具有任何類型,不傳回任何值。
*構造函數的功能是由使用者定義的,使用者根據初始化的要求設計函數體和函數參數。
例1
#include<iostream>
using namespace std;
class time
{
public:
time() //定義構造成員函數,函數名與類名相同
{
hour = 0;
minute = 0;
sec = 0;
}
void set_time(); //函數聲明
void show_time(); //函數聲明
private:
int hour;
int minute;
int sec;
};
void time::set_time() //定義成員函數,向資料成員指派
cin >> hour;
cin >> minute;
cin >> sec;
}
void time::show_time() //定義成員函數,向資料成員指派
cout << hour << ":" << minute << ":" << sec << endl;
int main()
time t1; //建立對象t1,同時調用構造函數t1.time()
t1.set_time(); //對t1的資料成員指派
t1.show_time(); //顯示t1的資料成員的值
time t2;
t2.show_time();
return 0;
程式運作的情況為:
10 25 54↙ (從鍵盤輸入新值賦給t1的資料成員)
10:25:54 (輸出t1的時、分、秒值)
0:0:0 (輸出t2的時、分、秒值)
有關構造函數的使用,有以下說明:
(1) 在類對象進入其作用域時調用構造函數。
(2) 構造函數沒有傳回值,是以也不需要在定義構造函數時聲明類型,這是它和一般函數的一個重要的不同之點。
(3) 構造函數不需使用者調用,也不能被使用者調用。
(4) 在構造函數的函數體中不僅可以對資料成員賦初值,而且可以包含其他語句。但是一般不提倡在構造函數中加入與初始化無關的内容,以保持程式的清晰。
(5) 如果使用者自己沒有定義構造函數,則c++系統會自動生成一個構造函數,隻是這個構造函數的函數體是空的,也沒有參數,不執行初始化操作。
二、帶參數的構造函數
在例一中構造函數不帶參數,在函數體中對資料成員賦初值。這種方式使該類的每一個對象都得到同一組初值(例如例一中各資料成員的初值均為0)。但是有時使用者希望對不同的對象賦予不同的初值。可以采用帶參數的構造函數,在調用不同對象的構造函數時,從外面将不同的資料傳遞給構造函數,以實作不同的初始化。
構造函數首部的一般格式為
構造函數名(類型 1 形參1,類型2 形參2,…)
由于使用者是不能調用構造函數的,是以無法采用正常的調用函數的方法給出實參。實參是在定義對象時給出的。定義對象的一般格式為
類名 對象名(實參1,實參2,…);
例2有兩個長方柱,其長、寬、高分别為: (1)12,20,25;(2)10,14,20。求它們的體積。編一個基于對象的程式,在類中用帶參數的構造函數。
class box
box(int,int,int); //聲明帶參數的構造函數
int volume(); //聲明計算體積的函數
int height;
int width;
int length;
box::box(int h,int w,int len) //在類外定義帶參數的構造函數
height = h;
width = w;
length = len;
int box::volume() //定義計算體積的函數
return (height*width*length);
box box1(12,25,30); //建立對象box1,并指定box1長、寬、高的值
cout << "the volume of box1 is " << box1.volume() << endl;
box box2(15,30,21);
cout << "the volume of box2 is " << box2.volume() << endl;
程式運作結果如下:
the volume of box1 is 9000
the volume of box2 is 9450
可以知道:
(1) 帶參數的構造函數中的形參,其對應的實參在定義對象時給定。
(2) 用這種方法可以友善地實作對不同的對象進行不同的初始化。
三、用參數初始化表對資料成員初始化
在二節中介紹的是在構造函數的函數體内通過指派語句對資料成員實作初始化。c++還提供另一種初始化資料成員的方法——參數初始化表來實作對資料成員的初始化。這種方法不在函數體内對資料成員初始化,而是在函數首部實作。例如例二中定義構造函數可以改用以下形式:
box∷box(int h,int w,int len):height(h),width(w),length(len){ }
這種寫法友善、簡練,尤其當需要初始化的資料成員較多時更顯其優越性。甚至可以直接在類體中(而不是在類外)定義構造函數。
四、構造函數的重載
在一個類中可以定義多個構造函數,以便對類對象提供不同的初始化的方法,供使用者選用。這些構造函數具有相同的名字,而參數的個數或參數的類型不相同。這稱為構造函數的重載。通過下面的例子可以了解怎樣應用構造函數的重載
例3在例2的基礎上,定義兩個構造函數,其中一個無參數,一個有參數。
box(); //聲明一個無參的構造函數
box(int h,int w,int len):height(h),width(w),length(len){}
//聲明一個有參的構造函數,用參數的初始化表對資料成員初始化
int volume();
box::box() //定義一個無參的構造函數
height = 10;
width = 10;
length = 10;
int box::volume()
box box1; //建立對象box1,不指定實參
box box2(15,30,25); //建立對象box2,指定3個實參
在本程式中定義了兩個重載的構造函數,其實還可以定義其他重載構造函數,其原型聲明可以為
box∷box(int h); //有1個參數的構造函數
box∷box(int h,int w); //有兩個參數的構造函數
在建立對象時分别給定1個參數和2個參數。
說明:
(1) 調用構造函數時不必給出實參的構造函數,稱為預設構造函數(default constructor)。顯然,無參的構造函數屬于預設構造函數。一個類隻能有一個預設構造函數。
(2) 如果在建立對象時選用的是無參構造函數,應注意正确書寫定義對象的語句。
(3) 盡管在一個類中可以包含多個構造函數,但是對于每一個對象來說,建立對象時隻執行其中一個構造函數,并非每個構造函數都被執行。
六、使用預設參數的構造函數
構造函數中參數的值既可以通過實參傳遞,也可以指定為某些預設值,即如果使用者不指定實參值,編譯系統就使形參取預設值。
例3的問題也可以使用包含預設參數的構造函數來處理。
例4 将例3程式中的構造函數改用含預設值的參數,長、寬、高的預設值均為10。
在例3程式的基礎上改寫如下:
例4
#include <iostream>
box(int h=10,int w=10,int len=10); //在聲明構造函數時指定預設參數
box::box(int h,int w,int len) //在定義函數時可以不指定預設參數
box box1; //沒有給實參
box box2(15); //隻給定一個實參
box box3(15,30); //隻給定2個實參
cout << "the volume of box3 is " << box3.volume() << endl;
box box4(15,30,20); //給定3個實參
cout << "the volume of box4 is " << box4.volume() << endl;
程式運作結果為
the volume of box1 is 1000
the volume of box2 is 1500
the volume of box3 is 4500
the volume of box4 is 9000
程式中對構造函數的定義(第12~16行)也可以改寫成參數初始化表的形式:
box∷box(int h,int w,int len):height(h),width(w),length(len){ }
可以看到: 在構造函數中使用預設參數是友善而有效的,它提供了建立對象時的多種選擇,它的作用相當于好幾個重載的構造函數。它的好處是: 即使在調用構造函數時沒有提供實參值,不僅不會出錯,而且還確定按照預設的參數值對對象進行初始化。尤其在希望對每一個對象都有同樣的初始化狀況時用這種方法更為友善。
(1) 應該在聲明構造函數時指定預設值,而不能隻在定義構造函數時指定預設值。
(2) 程式第5行在聲明構造函數時,形參名可以省略。
(3) 如果構造函數的全部參數都指定了預設值,則在定義對象時可以給一個或幾個實參,也可以不給出實參。
(4) 在一個類中定義了全部是預設參數的構造函數後,不能再定義重載構造函數。