目的:為了使用戶定義的數據類型如同一般數據類型一樣使用。
功能:對對象進行初始化,有若干種方法。
特點:1、無返回值說明;2、不能繼承;3、可以有缺省參數;4、不能取地址,構造時自動給對象分配棧中內存,析構時回收;
5、自動調用構造函數。
構造函數是類的一個特殊成員函數,其函數名同類名一樣。C++通過構造函數可以正確地初始化對象。構造函數不能被顯式調用,不能使
虛函數。
例: class queue {
int q[100]; //默認為private
int sloc,rloc;
public:
queue();
void qput(int i);
};
queue ::queue () //隱式調用
{
sloc=rloc=0;
}
int queue ::qput (int i){}
main()
{
queue a,b;
a.qput (10);
b.qput (20);
}
參數化的構造函數:
queue ::queue (int vx,int vy){} };
main()
{
queue a(10,20);
}
缺省參數的構造函數:特殊情況下需要傳遞參數,一般都是用缺省參數。
單個參數:queue ::queue (int i=16) {} };
main()
{
queue a;
}
多個參數:queue ::queue (int vx=0,int vy=0) {}
缺省參數還可用于一般成員函數。使用時應注意避免二義性。
多構造函數:
public:
queue ( );
queue (int );
queue (int,char );
};
main()
{
queue a;
queue b(1);
queue c(1,'c'); //避免二義性
}
拷貝構造函數:
1、系統產生:
queue ::queue (int vx,int vy){} };
main() { queue a(b) ; }
2、自定義:
queue ::queue (const queue &p){} };
main() { queue }
總結:構造函數的作用是對對象本身做初始化工作,也就是給用戶提供初始化類中成員變量的一種方式。
如果一個類中沒有定義任何的構造函數,那C++編輯器將在某些情況下提供一個默認的構造函數(不帶參數),3種情況:
1)、類有虛擬成員函數或虛擬繼承父類(虛擬基類);
2)、類的基類有構造函數;
3)、類中的所有非靜態餓對象數據成員,它們所屬的類中有構造函數。
構造函數的目的是為了初始化對象,因此一個構造函數至少應該
使得對象處于明確定義的狀態。
例://class string
string () {s=new char[80];len=80;}
string (int n) {s=new char[n];len=n;}
}
string::stringprintf() { cout<< s <<endl;}
定義對象:
string x,y(80);
x.print();
y.print();
此時,x和y調用的print()函數結構未定義,因為在構造函數中只對字符數組分配了內存,卻未對分配的內存進行初始化。
我們可以通過修改帶默認參數值的構造函數來改進:
string (int n=80) {s=new char[n];s[0]='\0';len=n;}
用默認參數的形式來代替函數重載的形式。
注意點:
1)構造函數應該使對象處于明確定義的狀態;
2)保持物理狀態的一致性:對數據成員的定義保持一致,在所有函數中只能使用一種定義。
3)類不變性:可以將不變性作為程序代碼的注釋,//len=strlen(s);
4)動態內存的一致性:接口一致性
void assign (char* str) { strcpy(s,str);}
void concat (string& a) {s=new char[len+1];strcpy(s,a.s);}
兩函數的表現行為存在不一致性:前者內存不再分配,而后者一直在分配。我們應只使用一種以保持一致性。
5)內存泄露:concat函數中每拷貝一次,s就重新分配一次,s被新的指針值覆蓋,而前一指針值被拋棄,產生內存垃圾。
因此concat函數必須保證舊的數組一定要被刪除,對于每一個new,就必須有一個delete操作,且delete語句只能被增加在
新的字符串創建之后。
void concat (string& a) {new_s=new char[len+1];strcpy(s,a.s); delete[]s;s=new_s;}