C++---對象的構造
Q:對象中成員變量的初始值是多少?
#include <iostream> using namespace std; class Test { private: int i; int j; public: int getI() { return i; } int getJ() { return j; } }; int main() { Test t1; cout<<"t1.getI()="<<t1.getI()<<endl; cout<<"t1.getJ()="<<t1.getJ()<<endl; }
輸出的結果為
#include <iostream> using namespace std; class Test { private: int i; int j; public: int getI() { return i; } int getJ() { return j; } }; Test gt; int main() { cout<<"gt.getI()="<<gt.getI()<<endl; cout<<"gt.getJ()="<<gt.getJ()<<endl; Test t1; cout<<"t1.getI()="<<t1.getI()<<endl; cout<<"t1.getJ()="<<t1.getJ()<<endl; Test* pt = new Test; cout<<"pt->getI()="<<pt->getI()<<endl; cout<<"pt->getJ()="<<pt->getJ()<<endl; delete pt; return 0; }
結果如圖
小結
從程序設計的角度,對象只是變量
1.在棧上創建對象時,成員變量初始為隨機值
2.在堆上創建對象時,成員變量初始值為隨機值
3.在靜態區創建對象時,成員變量初始值為0值
解決方案:
1.在類中提供一個public的intialize函數
2.對象創建後立即調用intialize函數進行初始化
示例
#include <iostream> using namespace std; class Test { private: int i; int j; public: int getI() { return i; } int getJ() { return j; } void initialize() { i = 1; j = 2; } }; Test gt; int main() { gt.initialize(); cout<<"gt.getI()="<<gt.getI()<<endl; cout<<"gt.getJ()="<<gt.getJ()<<endl; Test t1; //t1.initialize(); cout<<"t1.getI()="<<t1.getI()<<endl; cout<<"t1.getJ()="<<t1.getJ()<<endl; t1.initialize(); Test* pt = new Test; pt->initialize(); cout<<"pt->getI()="<<pt->getI()<<endl; cout<<"pt->getJ()="<<pt->getJ()<<endl; delete pt; return 0; }
運行結果
Q:當intialize的位置出現了改變的話,還會對其進行初始化嗎
輸出結果如圖所示,發現intialize位置改變了之後,初始化也出現了問題
存在的問題
1.intialize只是一個普通函數,必須顯示調用
2.如果為調用intialize函數,運行結果是不確定的
解決方法
C++中可以定義與類名相同的特殊成員函數--這種特殊的成員函數叫做構造函數
1.構造函數沒有任何返回類型的聲明
2.構造函數在對象定義時自動被調用
示例
#include <iostream>
using namespace std;
class Test
{
private:
int i;
int j;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
Test()
{
printf("Test() Begin\n");
i = 1;
j = 2;
printf("Test() End\n");
}
};
Test gt;
int main()
{
cout<<"gt.getI()="<<gt.getI()<<endl;
cout<<"gt.getJ()="<<gt.getJ()<<endl;
Test t1;
cout<<"t1.getI()="<<t1.getI()<<endl;
cout<<"t1.getJ()="<<t1.getJ()<<endl;
t1.initialize();
Test* pt = new Test;
cout<<"pt->getI()="<<pt->getI()<<endl;
cout<<"pt->getJ()="<<pt->getJ()<<endl;
delete pt;
return 0;
}
運行結果
小結
1.每個對象在使用之前都應該初始化
2.類的構造函數用於對象的初始化
3.構造函數與類同名並且沒有返回值
4.構造函數在對象定義時自動被調用
二.對象的構造(中)
帶有參數的構造函數
1.構造函數可以根據需要定義參數
2.一個類中可以存在多個重載的構造函數
3.構造函數的重載遵循C++重載的規則
對象定義和對象聲明不同
1.對象定義--申請對象的空間並調用構造函數
2.對象聲明--告訴編譯器存在這樣一個對象
示例
#include <iostream>
using namespace std;
class Test
{
public:
Test()
{
cout<<"Test()"<<endl;;
}
Test(int v)
{
cout<<"Test(int v)="<<v<<endl;
}
};
int main()
{
Test t; // 調用 Test()
Test t1(1); // 調用 Test(int v)
Test t2 = 2; // 調用 Test(int v)
int i(100);
cout<<"i="<<i<<endl;
return 0;
}
結果
小結
1.構造函數可以根據需要定義參數
2.構造函數之間可以存在重載關系
3.構造函數遵循C++中重載函數的規則
4.對象定義時會觸發構造函數的調用
5.在一些情況下可以手動調用構造函數
三.特殊的構造函數
1.無參構造函數--沒有參數的構造函數,當類中沒有定義構造函數時,編譯器默認提供一個無參構造函數,並且其函數體為空
2.拷貝構造函數--參數為const class_name&的構造函數,當類中沒有定義拷貝構造函數時,編譯器默認提供一個拷貝構造函數,簡單的進行成員變量的值的復制
拷貝構造函數的意義
1.兼容C語言的初始化方式
2.初始化行為能夠符合預期的邏輯
3.淺拷貝--拷貝後的物理狀態相同
4.深拷貝--拷貝後的邏輯狀態相同
小結
1.C++編譯器會默認提供構造函數
2.無參構造函數用於定義對象的默認初始狀態‘
3.拷貝構造函數在創建對象時拷貝對象的狀態
4.對象的拷貝由淺拷貝和深拷貝兩種方式
C++---對象的構造