對於一個空類,編譯器默認產生4個成員函數:默認構造函數、析構函數、拷貝構造函數和賦值函數。
1、構造函數:
構造函數是一種特殊的類成員,是當創建一個類的時候,它被調用來對類的數據成員進行初始化和分配內存。構造函數的命名必須和類名完全相同,構造函數可以被重載,可以多個,可以帶參數。
eg:
class A
{
public:
A(){cout<<"無參構造函數";}
A(int i){cout<<"帶參構造函數";}
};
A();//調用默認構造函數
A(1);//調用有參構造函數
創建一個對象就會調用無參構造函數,也就是默認構造函數,有參的構造函數需要自己調用。
2、析構函數
構造函數可以重載,析構函數不能重載,析構函數永遠只有一個,如果沒寫析構函數,c++會自動幫我們寫一個析構函數。
構造函數和析構函數是一對,構造函數用來創建對象,而析構函數是用來撤銷對象。
eg:
#include <iostream>
using namespace std; class A { public: A() { cout<<"構造函數被調用了!"<<endl; } ~A(){cout<<"析構函數被調用了"<<endl;} }; int main() { A a;//調用構造函數和析構函數
A *p=new A;//調用構造函數
delete p;//調用析構函數
return 0; }
3、拷貝構造函數
c++拷貝構造函數(深拷貝、淺拷貝)
4、賦值函數
當一個類的對象向該類的另一個對象賦值的時候,就會用到該函數。
當沒有重載賦值函數,通過默認的賦值函數進行賦值操作。
eg:
#include <iostream>
using namespace std; class A { public: A(){cout<<"調用構造函數"<<endl;} A(char s){cout<<s<<"調用構造函數"<<endl;} ~A(){cout<<"調用析構函數"<<endl;} }; int main() { A a('a'); A b('b'); b=a;//a,b實際存在,再調用賦值函數,將a賦值給b
A c(a);//拷貝構造函數
return 0; }
拷貝函數和賦值函數的差別:
1、拷貝構造函數是一個對象初始化一塊內存區域,這塊內存就是新對象的內存區,而賦值函數是對於一個已經被初始化的對象進行賦值操作。
2、一般來說在數據成員包含指針對象的時候,需要考慮兩種不同的處理需求:一種是復制指針對象,另一種是引用指針對象。拷貝構造函數大多數情況下是復制,而賦值函數是引用函數。
3、實現不一樣。拷貝構造函數首先是一個構造函數,它調用的時候是通過參數的對象初始化產生一個對象。賦值函數則是把一個新的對象賦值給一個原有的對象,所以如果原來的對象中有內存分配要先把內存釋放掉,而且還要檢查一下兩個對象是不是同一個對象,如果是,不做任何操作,直接返回。
#include <iostream>
using namespace std; class A { public: A(){cout<<"調用構造函數"<<endl;} A(char s){cout<<s<<"調用構造函數"<<endl;} A(const A &other); A & operator = (const A &a);//重載拷貝函數
~A(){cout<<"調用析構函數"<<endl;} }; A& A::operator =(const A &a) { A example; cout<<"調用拷貝函數"<<endl; return example; } int main() { A a('a'); A b('b'); b=a;//a,b實際存在,再調用賦值函數,將a賦值給b
return 0; }
總結:
對象不存在,且用別的對象來初始化,就是調用了構造函數
對象不存在,且用別的對象來初始化,就是拷貝構造函數
對象存在,用別的對象來給它賦值,就是賦值函數
程序結束,每個對象都會調用析構函數
