出處:http://blog.chinaunix.net/uid-24219701-id-1993923.html
對象的初始化在建立一個對象時,常常需要作某些初始化的工作,例如對數據成員賦初值。
如果一個數據成員未被賦值,則它的值是不可預知的,因爲在系統爲它分配內存時,保留了這些存儲單元的原狀,這就成爲了這些數據成員的初始值。
這種狀況顯然是與人們的要求不相符的,對象是一個實體,它反映了客觀事物的屬性(例如時鐘的時、分、秒的值),是應該有確定的值的。
注意:類的數據成員是不能在聲明類時初始化的。
如果一個類中所有的成員都是公用的,則可以在定義對象時對數據成員進行初始化。
如
class Time
{public : //聲明爲公用成員
hour;
minute;
sec;
};
Time t1={14,56,30}; //將t1初始化爲:56:30
這種情況和結構體變量的初始化是差不多的,在一個花括號內順序列出各公用數據成員的值,
兩個值之間用逗號分隔。
但是,如果數據成員是私有的,或者類中有private或protected的成員,
就不能用這種方法初始化。
在第章的幾個例子中,是用成員函數來對對象中的數據成員賦初值的(例如例.3中的set_time函數)。
從例.3中可以看到,用戶在主函數中調用set_time函數來爲數據成員賦值。
如果對一個類定義了多個對象,而且類中的數據成員比較多,那麼,程序就顯得非常臃腫煩瑣。
9.1.2 構造函數的作用
爲了解決這個問題,C++提供了構造函數(constructor)來處理對象的初始化。
構造函數是一種特殊的成員函數,與其他成員函數不同,不需要用戶來調用它,而是在建立對象時自動執行。
構造函數的名字必須與類名同名,而不能由用戶任意命名,以便編譯系統能識別它並把它作爲構造函數處理。
它不具有任何類型,不返回任何值。
構造函數的功能是由用戶定義的,用戶根據初始化的要求設計函數體和函數參數。
例.1 在例.3基礎上定義構造成員函數。
#include
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<<< span="" style="word-wrap: break-word;">″:″<<minute<<< span="" style="word-wrap: break-word;">″:″<
}
int main( )
{
Time t1;
t1.set_time( );
t1.show_time( );
Time t2;
t2.show_time( );
return 0;
}
程序運行的情況爲:
10 25 54↙
10:25:54
0:0:0
(從鍵盤輸入新值賦給t1的數據成員) (輸出t1的時、分、秒值) (輸出t2的時、分、秒值)
上面是在類內定義構造函數的,也可以只在類內對構造函數進行聲明而在類外定義構造函數。
將程序中的第~7行改爲下面一行:
Time( ); //對構造函數進行聲明
在類外定義構造函數:
Time::Time( ) //在類外定義構造成員函數,要加上類名Time和域限定符“::”
{hour=0;
minute=0;
sec=0;
}
有關構造函數的使用,有以下說明:
(1) 在類對象進入其作用域時調用構造函數。
(2)構造函數沒有返回值,因此也不需要在定義構造函數時聲明類型,這是它和一般函數的一個重要的不同之點。
(3) 構造函數不需用戶調用,也不能被用戶調用。
(4) 在構造函數的函數體中不僅可以對數據成員賦初值,而且可以包含其他語句。
但是一般不提倡在構造函數中加入與初始化無關的內容,以保持程序的清晰。
(5) 如果用戶自己沒有定義構造函數,則C++系統會自動生成一個構造函數,只是這個構造函數的函數體是空的,也沒有參數,不執行初始化操作。
9.1.3 帶參數的構造函數
在例.1中構造函數不帶參數,在函數體中對數據成員賦初值。
這種方式使該類的每一個對象都得到同一組初值(例如例.1中各數據成員的初值均爲)。
但是有時用戶希望對不同的對象賦予不同的初值。
可以採用帶參數的構造函數,在調用不同對象的構造函數時,從外面將不同的數據傳遞給構造函數,以實現不同的初始化。
構造函數首部的一般格式爲構造函數名(類型1 形參,類型形參,…) 前面已說明:用戶是不能調用構造函數的,因此無法採用常規的調用函數的方法給出實參。
實參是在定義對象時給出的。
定義對象的一般格式爲類名對象名(實參,實參,…);
例.2 有兩個長方柱,其長、寬、高分別爲:
(1)12,20,25;
(2)10,14,20。
求它們的體積。
編一個基於對象的程序,在類中用帶參數的構造函數。
#include
using namespace std;
class Box
{public :
Box(int,int,int);
int volume( );
private :
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);
}
int main( )
{
Box box1(12,25,30); //建立對象box1,並指定box1長、寬、高的值
cout<<″The volume of box1 is ″<
Box box2(15,30,21); //建立對象box2,並指定box2長、寬、高的值
cout<<″The volume of box2 is ″<
return 0;
}
程序運行結果如下:
The volume of box1 is 9000
The volume of box2 is 9450
可以知道:
()帶參數的構造函數中的形參,其對應的實參在定義對象時給定。
()用這種方法可以方便地實現對不同的對象進行不同的初始化。
9.1.4 用參數初始化表對數據成員初始化
在.1.3節中介紹的是在構造函數的函數體內通過賦值語句對數據成員實現初始化。
C++還提供另一種初始化數據成員的方法——參數初始化表來實現對數據成員的初始化。
這種方法不在函數體內對數據成員初始化,而是在函數首部實現。
例如例.2中定義構造函數可以改用以下形式:
Box::Box(int h,int w,int len):height(h),width(w),length(len){ }
這種寫法方便、簡練,尤其當需要初始化的數據成員較多時更顯其優越性。
甚至可以直接在類體中(而不是在類外)定義構造函數。
9.1.5 構造函數的重載
在一個類中可以定義多個構造函數,以便對類對象提供不同的初始化的方法,供用戶選用。
這些構造函數具有相同的名字,而參數的個數或參數的類型不相同。
這稱爲構造函數的重載。
在第章第.6節中所介紹的函數重載的知識也適用於構造函數。
通過下面的例子可以瞭解怎樣應用構造函數的重載。
例.3 在例.2的基礎上,定義兩個構造函數,其中一個無參數,一個有參數。
#include
using namespace std;
class Box
{public : Box( ); //聲明一個無參的構造函數
Box(int h,int w,int len):height(h),width(w),length(len){ } //聲明一個有參的構造函數,用參數的初始化表對數據成員初始化
int volume( );
private :
int height;
int width;
int length;
};
Box::Box( ) //定義一個無參的構造函數
{
height=10; width=10; length=10;
}
int Box::volume( )
{
return (height*width*length);
}
int main( )
{
Box box1; //建立對象box1,不指定實參
cout<<″The volume of box1 is ″<
Box box2(15,30,25); //建立對象box2,指定個實參
cout<<″The volume of box2 is ″<
return 0;
}
在本程序中定義了兩個重載的構造函數,其實還可以定義其他重載構造函數,其原型聲明可以爲
Box::Box(int h);//有個參數的構造函數
Box::Box(int h,int w);//有兩個參數的構造函數
在建立對象時分別給定個參數和個參數。
說明:
(1) 調用構造函數時不必給出實參的構造函數,稱爲默認構造函數(default constructor)。
顯然,無參的構造函數屬於默認構造函數。
一個類只能有一個默認構造函數。
(2) 如果在建立對象時選用的是無參構造函數,應注意正確書寫定義對象的語句。
(3) 儘管在一個類中可以包含多個構造函數,但是對於每一個對象來說,建立對象時只執行其中一個構造函數,並非每個構造函數都被執行。
9.1.6 使用默認參數的構造函數
構造函數中參數的值既可以通過實參傳遞,也可以指定爲某些默認值,即如果用戶不指定實參值,編譯系統就使形參取默認值。
在第章第.8節中介紹過在函數中可以使用有默認值的參數。
在構造函數中也可以採用這樣的方法來實現初始化。
例.3的問題也可以使用包含默認參數的構造函數來處理。
例.4 將例.3程序中的構造函數改用含默認值的參數,長、寬、高的默認值均爲。
在例.3程序的基礎上改寫如下:
#include
using namespace std;
class Box
{public :
Box(int h=10,int w=10,int len=10); //在聲明構造函數時指定默認參數
int volume( );
private :
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);
}
int main( )
{
Box box1; //沒有給實參
cout<<″The volume of box1 is ″<
Box box2(15); //只給定一個實參
cout<<″The volume of box2 is ″<
Box box3(15,30); //只給定個實參
cout<<″The volume of box3 is ″<
Box box4(15,30,20); //給定個實參
cout<<″The volume of box4 is ″<
return 0;
}
程序運行結果爲
The volume of box1 is 1000
The volume of box2 is 1500
The volume of box3 is 4500
The volume of box4 is 9000
程序中對構造函數的定義(第-16行)也可以改寫成參數初始化表的形式:
Box::Box(int h,int w,int len):height(h),width(w),length(len){ }
可以看到:在構造函數中使用默認參數是方便而有效的,它提供了建立對象時的多種選擇,
它的作用相當於好幾個重載的構造函數。
它的好處是:即使在調用構造函數時沒有提供實參值,不僅不會出錯,而且還確保按照
默認的參數值對對象進行初始化。
尤其在希望對每一個對象都有同樣的初始化狀況時用這種方法更爲方便。
說明:
(1) 應該在聲明構造函數時指定默認值,而不能只在定義構造函數時指定默認值。
(2) 程序第行在聲明構造函數時,形參名可以省略。
(3) 如果構造函數的全部參數都指定了默認值,則在定義對象時可以給一個或幾個實參,也可以不給出實參。
(4) 在一個類中定義了全部是默認參數的構造函數後,不能再定義重載構造函數。