構造析構
一、構造函數
1、介紹
構造函數,它是一種特殊的函數,主要用來在創建對象時初始化對象,即為對象的成員變量賦初始值。
2、構造函數的定義:
//1 構造函數名和類名相同
//2 構造函數沒有返回值類型,和返回值
//3 構造函數可以重載,需要滿足函數重載的條件
class student
{
public:
student(){} //無參構造(默認構造)
student(int a){} //有參構造(帶參構造)
};
3、構造函數的調用時機
//1 在創建一個新的對象的時候會調用
//調用無參構造
student stu;
student *p = new student;
//調用帶參構造
student stu(1);
student *p = new student(1);
4、構造函數的特點
/*
* 1 如果一個類中沒有顯示的給出構造函數,系統會自動給出一個缺省的(隱式)什么都不干的構造函數
* 2 如果類中有多個構造函數,那么通常會有不同的參數列表和函數體
* 3 如果用戶提供了無參(帶參)構造,那么系統就不再提供默認構造
* 4 類中如果只有帶參構造,沒有無參構造,那么就不能調用默認構造的方式初始化對象,想用這種方式初始化對象就要提供無參構造
*
*
*/
5、拷貝構造(一種特殊的構造)
//1 拷貝構造是一種特殊的構造函數,用自身這種類型來構造自身
//例;
student stu1;
student stu2 = sut1;//在這一句就會調用拷貝構造
拷貝構造的定義:
1 沒有寫拷貝構造,系統會提供一個隱式的拷貝構造,而這個拷貝構造的操作,我們可以理解為,是用 ‘ = ’ 號一個一個的將存在於對象中的數據成員賦值給新創建的對象中。
// 2 自定義拷貝構造
// 類名(const 類名& 引用名){}
// 這里的const不加也可以,但是這里是拷貝,也就是復制的意思,所以加上,防止被改
// 在函數體里面通常的做法是逐一拷貝傳進來的對象的成員,賦值給新的對象,當然也可以自己定義內容
//例:
class Student
{
int id;
public:
Student(const Student&stu)
{
this->id=stu.id;
//把傳進來對象的id賦值給當前調用對象的id
}
}
拷貝構造的調用:
//1 在用同種類的對象,去初始化另一個對象的時候(注意是在定義對象初始化,不是賦值)
Student stu1;
Student stu2=stu1;//顯示調用拷貝構造
Student stu3(stu2);//隱式調用拷貝構造
Student *pStu=new Student(stu3);
//2 在函數傳參時,函數的形參是類對象
void fun(Student stu){}
fun(stu1);//在函數調用傳參時調用拷貝構造
//3 如果一個函數的返回值類型是對象,在函數調用結束,返回對象的時候調用拷貝構造
Student fun1(Student stu)
{
return stu;
}
//這里在函數調用的時候會調用兩次拷貝構造,函數傳參一次,函數返回時一次
//函數返回時再次調用拷貝構造的原因:stu形參是屬於棧區的,不能return出來,出了函數就直接被釋放了,所以想要返回出來,就要再拷貝一次。
6、深拷貝與淺拷貝
1、淺拷貝
淺拷貝構造我們不寫,系統也會提供一個默認的拷貝構造,而這個拷貝構造的操作,我們可以理解為,是用 ‘ = ’ 號一個一個的賦值的,我們將之稱為,淺拷貝。在用指針的時候我們就知道兩個同等類型的指針之間用 ‘ = ’ 號賦值,是兩個指針的地址指向同一個內存,那么就可能會存在一個問 題,就是兩個對象的指針都指向同一個內存,那么如果其中一個對象把該內存釋放了,就會導致另外一個對象的指針變成野指針。
2、深拷貝
也就是在有上述問題的時候才需要深拷貝,而深拷貝做的事情也就是自己定義拷貝構造,給新的對象的指針申請內存,來存內容,而不是兩個對象的指針指向同一個內存
//示例:
class Student
{
char *name;
public:
Student(char *name)
{
this->name=new char[strlen(name)+1];//加1是為了保存'\0'
strcpy(this->name,name);
}
Student(const Student& stu)
{
//這個自定義拷貝構造函數做的事,其實就是給新對象的指針申請 內存來存數據,如果有其他成員,那么也要記得賦值
this->name=new char[strlen(stu.name)+1];//加1是為了存'\0'
strcpy(this->name,s
tu.name);
}
}
什么時候要自己寫拷貝構造?
類中有動態申請內存的時候,必須要寫。