來源:https://www.cnblogs.com/duwenxing/p/7445927.html
先來看一個例子:
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 class Student{ 5 public: 6 Student(){ 7 cout<<"調用默認構造函數"<<endl; 8 }; 9 Student(string name,int age,string gender):Name(name),Age(age),Gender(gender){ 10 //cout<<"調用構造函數1"<<endl; 11 } 12 Student(const Student& stu){//拷貝構造函數 13 Name=stu.Name; 14 Age=stu.Age; 15 Gender=stu.Gender; 16 cout<<"調用拷貝構造函數"<<endl; 17 } 18 ~Student(){ 19 //cout<<"調用析構函數"<<endl; 20 } 21 void show(){ 22 cout<<"Name:"<<Name<<endl; 23 cout<<"Age:"<<Age<<endl; 24 cout<<"Gender:"<<Gender<<endl; 25 } 26 private: 27 string Name; 28 int Age; 29 string Gender; 30 }; 31 32 int main(){ 33 Student stu("Tomwenxing",23,"male"); 34 Student stu2("Ellen",24,"female"); 35 cout<<"---------------------賦值操作之前-----------------"<<endl; 36 stu2.show(); 37 cout<<"---------------------賦值操作之後-----------------"<<endl; 38 stu2=stu; 39 stu2.show(); 40 return 0; 41 }
由上面的例子可以看出,C++支持自定義類型的對象之間的賦值操作,而賦值功能的實現則主要依靠自定義類中的賦值函數。每一個自定義類中都有且只有一個賦值函數,該賦值函數既可以由編譯器隱式地定義在自定義類中,也可以有用戶通過對賦值運算符=的重載顯式地定義在自定義類中:
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 class Student{ 5 public: 6 Student(){ 7 cout<<"調用默認構造函數"<<endl; 8 }; 9 Student(string name,int age,string gender):Name(name),Age(age),Gender(gender){ 10 //cout<<"調用構造函數1"<<endl; 11 } 12 Student(const Student& stu){//拷貝構造函數 13 Name=stu.Name; 14 Age=stu.Age; 15 Gender=stu.Gender; 16 cout<<"調用拷貝構造函數"<<endl; 17 } 18 ~Student(){ 19 //cout<<"調用析構函數"<<endl; 20 } 21 Student& operator=(const Student& stu){ //賦值函數 22 cout<<"調用類中的賦值函數"<<endl; 23 if(this!=&stu){ 24 Name=stu.Name; 25 Age=stu.Age; 26 Gender=stu.Gender; 27 } 28 return *this; 29 } 30 void show(){ 31 cout<<"Name:"<<Name<<endl; 32 cout<<"Age:"<<Age<<endl; 33 cout<<"Gender:"<<Gender<<endl; 34 } 35 private: 36 string Name; 37 int Age; 38 string Gender; 39 }; 40 41 int main(){ 42 Student stu("Tomwenxing",23,"male"); 43 Student stu2("Ellen",24,"female"); 44 cout<<"---------------------賦值操作之前-----------------"<<endl; 45 stu2.show(); 46 cout<<"---------------------賦值操作之後-----------------"<<endl; 47 stu2=stu; 48 stu2.show(); 49 return 0; 50 }
特別注意:
Question 1:類中的賦值函數中的參數爲什麼加const?
Answer:參數使用cosnt的原因有兩個:
• 防止類中的賦值函數對用來賦值的“原對象”進行修改
1 Student& Student::operator=(Student& stu){ 2 cout<<"調用類中的賦值函數"<<endl; 3 if(this!=&stu){ 4 stu.Name="none";//錯誤,對用來賦值的“原對象”進行了修改 5 stu.Age=0;//錯誤 6 stu.Gender="none";//錯誤 7 Name=stu.Name; 8 Age=stu.Age; 9 Gender=stu.Gender; 10 } 11 return *this; 12 }
•若賦值函數的形參加上const,則賦值函數接受的實參對象既可以是const對象,也可以是非const對象;否則賦值函數能夠接受的對象只能是非const對象,而不能是const對象。
1 Student& Student::operator=(Student& stu){ 2 cout<<"調用類中的賦值函數"<<endl; 3 if(this!=&stu){ 4 Name=stu.Name; 5 Age=stu.Age; 6 Gender=stu.Gender; 7 } 8 return *this; 9 } 10 int main(){ 11 const Student stu("Tomwenxing",23,"male"); 12 Student stu2("Ellen",24,"female"); 13 stu2=stu; //錯誤!不能將const對象賦值給非const對象 14 return 0; 15 }
Question 2:類中的賦值函數中的參數爲什麼使用引用?
Answer:避免調用類中的拷貝構造函數在內存中開闢空間來創建形參對象,而是讓形參成爲實參的別名,從而節省時間和空間,提供編程效率
1 Student& Student::operator=(const Student &stu){ //參數中使用引用 2 cout<<"調用類中的賦值函數"<<endl; 3 if(this!=&stu){ 4 Name=stu.Name; 5 Age=stu.Age; 6 Gender=stu.Gender; 7 } 8 return *this; 9 } 10 11 int main(){ 12 const Student stu("Tomwenxing",23,"male"); 13 Student stu2("Ellen",24,"female"); 14 stu2=stu; 15 return 0; 16 }
1 Student& Student::operator=(const Student stu){ //參數中沒有使用引用 2 cout<<"調用類中的賦值函數"<<endl; 3 if(this!=&stu){ 4 Name=stu.Name; 5 Age=stu.Age; 6 Gender=stu.Gender; 7 } 8 return *this; 9 } 10 11 int main(){ 12 const Student stu("Tomwenxing",23,"male"); 13 Student stu2("Ellen",24,"female"); 14 stu2=stu; 15 return 0; 16 }
Question 3:類中的賦值函數的返回值類型爲什麼是Student&,不可以是Student或void嗎?
Answer:在C++中,系統支持變量之間的連續賦值,如:
1 int a=10; 2 int b,c; 3 b=c=a;//變量之間的連續賦值,b、c的值均爲10
其本質是先將變量a的值賦值給變量c,然後將賦值後的變量c返回到賦值運算符=的右邊,再將其賦值給變量b,即:
1 int a=10; 2 b=(c=a); //即c=a,b=c;
同理,如果類中賦值函數的返回值類型爲void,即類中的賦值函數沒有返回值,此時編譯器將不支持對該類中的對象進行連續賦值
1 void Student::operator=(const Student &stu){ 2 cout<<"調用類中的賦值函數"<<endl; 3 if(this!=&stu){ 4 Name=stu.Name; 5 Age=stu.Age; 6 Gender=stu.Gender; 7 } 8 } 9 int main(){ 10 const Student stu("Tomwenxing",23,"male"); 11 Student stu2("Ellen",24,"female"); 12 Student stu3; 13 stu3=stu2=stu; //錯誤!stu3=stu2=stu相當於stu3.operator=(stu2.operator=(stu)) ,由於賦值函數沒有返回值,則該語句無法執行 14 return 0; 15 }
而賦值函數的返回值類型之所以是Student&而非Student是爲了避免函數在返回對象時調用類中的拷貝構造函數在內存中創建臨時對象,從而提高程序的運行效率
1 Student& Student::operator=(const Student& stu){ //返回值類型中使用引用& 2 cout<<"調用類中的賦值函數"<<endl; 3 if(this!=&stu){ 4 Name=stu.Name; 5 Age=stu.Age; 6 Gender=stu.Gender; 7 } 8 return *this; 9 } 10 11 int main(){ 12 Student stu("Tomwenxing",23,"male); 13 Student stu2; 14 stu2=stu; 15 return 0; 16 }
1 Student Student::operator=(const Student& stu){ //返回值類型中沒有使用引用& 2 cout<<"調用類中的賦值函數"<<endl; 3 if(this!=&stu){ 4 Name=stu.Name; 5 Age=stu.Age; 6 Gender=stu.Gender; 7 } 8 return *this; 9 } 10 11 int main(){ 12 Student stu("Tomwenxing",23,"male"); 13 Student stu2; 14 stu2=stu; 15 return 0; 16 }
Question 4:語句“if(this!=&stu)”的作用是什麼?
Answer:通過比較賦值者和被賦值者的地址是否相同來判斷兩者是否是同一個對象,從而避免自賦值(即自己給自己賦值)的情況的發生,原因如下:
• 爲了提高程序的運行效率。自己給自己賦值是完全無意義的行爲,只會浪費程序的時間資源。
• 當類中的數據成員中含有指針時,自賦值操作可能會帶來災難性的後果。例如假設對象a和b中都含有一個指針ptr,它們分別指向一塊通過new動態開闢的內存空間A和內存空間B,當將對象a賦值給對象b時,要求先將對象b中指針ptr指向的內存空間B通過delete釋放掉(否則將造成內存泄漏),然後在內存中重新開闢內存空間C以拷貝內存空間A中的內容,並讓對象b的指針ptr重新指向內存空間C,從而完成賦值。如果此時允許對象的自賦值,那麼對象會在自賦值前先釋放自己指針所指的內存空間,然後重新在內存中開闢空間,然而由於之前的內存空間已經釋放,此時新內存空間希望拷貝的內容將不復存在,因而造成災難性後果。
因此,對於類中的賦值函數,一定要先檢查是否是自賦值,如果是,直接return *this。
Question 5:自定義類的對象之間的賦值操作的本質是什麼?
Answer:本質是調用類中的成員函數(operator=()函數)。如:
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 class Student{ 5 public: 6 Student(){ 7 //cout<<"調用默認構造函數"<<endl; 8 }; 9 Student(string name,int age,string gender):Name(name),Age(age),Gender(gender){ 10 //cout<<"調用構造函數1"<<endl; 11 } 12 Student(const Student& stu){//拷貝構造函數 13 Name=stu.Name; 14 Age=stu.Age; 15 Gender=stu.Gender; 16 cout<<"調用拷貝構造函數"<<endl; 17 } 18 ~Student(){ 19 //cout<<"調用析構函數"<<endl; 20 } 23 if(this!=&stu){ 24 Name=stu.Name; 25 Age=stu.Age; 26 Gender=stu.Gender; 27 } 28 return *this; 29 } 30 void show(){ 31 cout<<"\tName:"<<Name<<endl; 32 cout<<"\tAge:"<<Age<<endl; 33 cout<<"\tGender:"<<Gender<<endl; 34 } 35 private: 36 string Name; 37 int Age; 38 string Gender; 39 }; 40 41 int main(){ 42 Student stu("Tomwenxing",23,"male"); 43 Student stu2,stu3; 44 stu2=stu;//對stu2進行賦值 45 cout<<"對象stu2:"<<endl; 46 stu2.show(); 47 cout<<"------------分界線-------------------"<<endl; 48 stu3.operator=(stu);//對stu3進行賦值 49 cout<<"對象stu3:"<<endl; 50 stu3.show(); 51 return 0; 52 }