關於構造,析構,複製構造,=,和自動創建無名變量
總結:
1.在新定義對象時,=調用複製構造函數,而對已有對象,=是運算符=
2.在計算時,如果類型不符,系統強制類型轉換,調用最接近的構造函數,即 無名對象(a) ,在完成此運算符對應的計算後,該無名變量就被析構掉
例1:
#include<iostream> using namespace std; class A{ float x,y; public: A(){ x=0; y =0; cout<<"調用無參構造函數\n";} A(float a){ x=a ; y= 0; cout<<"調用一個參數的構造函數\n"; } A(float a,float b){ x=a; y =b; cout<<"調用兩個參數的構造函數\n";} A(A &a){ x= a.x ; y = a.y; cout<<"調用複製構造函數\n";} ~A(){cout<<"調用析構函數\n";} //A & operator = (A & a){ x=a.x; y =a.y; cout<<"調用了運算符=的重載\n";} void Print (){ cout<<x<<'\t'<<y<<endl;} }; void f(){ A a0; //此處若用 A a0 = 2+3; 則會報錯 //因爲對象定義時, = 採用的是複製構造函數,沒有相一致的複製構造函數對應 a0 = 2+3; //定義構造函數時, A a0(2,3); 也可以寫成 A a0 = A(2,3); a0.Print(); } A a1(7.0,3.0); //主函數前的對象,先調用兩個參數的構造函數 int main(){ f(); // A a0 調用無參構造函數 // 2+3 = 5 一般計算 // 強制類型轉換,臨時對象(5) 生成臨時對象,用數值5對其構造,調用一個參數的構造函數 // 利用"=",a0調用複製構造函數,複製臨時對象(5)這個對象 // Print()函數打印 // 第一個析構的是無名對象,第二個析構的是a0,注意無名變量析構時也會調用析構函數 A a2(a1); // 利用構造函數的一般形式,對a2構造,複製對象爲a1 // 第三個析構是對a2的析構,第四個析構是對全局對象a1的析構 return 0; }
結果:
調用兩個參數的構造函數
調用無參構造函數
調用一個參數的構造函數
調用析構函數
5 0
調用析構函數
調用複製構造函數
調用析構函數
調用析構函數
例2:
#include<iostream> using namespace std; int MMMax=0; class A{ int x,y; static int count; public: A(int a = 0 ,int b = 0){ x = a ; y = b ; MMMax = ++count; cout<<"調用了兩個參數的構造函數\n"; } A(A &c){ x=c.x; y=c.y; MMMax = ++count; cout<<"調用了複製構造函數\n"; } ~A(){ count--; cout<<"調用了析構函數\n";} int getnum(){ return count;} }; int A::count = 0; int main(){ A a1,a2(10,20); // a1 兩個默認值的構造函數 a2 帶有兩個參數的構造函數 // count 作爲靜態變量,調用構造函數時+1,所以這裏0+2=2 cout<<"count="<<a2.getnum()<<endl; A a3=a1; // 利用複製構造函數,直接將a1的內容複製給a3,中間沒有無名對象過程,複製的過程取決於複製構造函數 // 當對象新定義時,=調用複製構造函數 cout<<"count="<<a3.getnum()<<endl; a1 = A(20,30); // 強制類型轉換,用兩個參數的構造函數構造無名對象 // a1是已有的對象,所以利用運算符=,將無名對象複製給a1 // 無名對象調用析構函數析構,在結束複製過程後即析構 // 調用析構函數時會用count--,所以MMMax和count的值不一樣 cout<<"count="<<a1.getnum()<<endl; cout<<"MMMax="<<MMMax<<endl; A a4=a2; // 當對象新定義時,=調用複製構造函數 a4 = a2; // 當對象是已有時,=調用運算符=的重載 // 析構函數的順序分別是a4,a3,a2,a1 return 0; }
結果:
調用了兩個參數的構造函數
調用了兩個參數的構造函數
count=2
調用了複製構造函數
count=3
調用了兩個參數的構造函數
調用了析構函數
count=3
MMMax=4
調用了複製構造函數
調用了析構函數
調用了析構函數
調用了析構函數
調用了析構函數
例3:
#include<iostream> using namespace std; char gid = 'A'; class Number{ private: int i; char id; public: Number(int x=0){ i = x; id =gid++; cout<<"Constructor Number:"<<id<<i<<endl; } Number( const Number &x ){ i=x.i; id =gid ++; cout<<"Copy Number:"<<id<<i<<endl; } ~Number(){cout<<"Destructor Number:"<<id<<i<<endl;} Number operator+(const Number &x); }; Number Number::operator+(const Number &x) { Number result(i+x.i); return result; } int main(){ Number x(1),y; //調用一個參數的構造函數構造x,調用默認值的構造函數構造y y = x+2; //數字2強制類型轉換,生成臨時對象,用一個參數的構造函數構造來轉換,無名變量(2) //重載運算符+進行計算,用一個參數的構造函數構造新對象,並在運算結束後析構,重載運算符+中不涉及無名變量等問題 //如果將運算符+重載的內容直接改成 i=x.i; return *this; 則顯示結果中Construtor:D3這一句改成Copy Number:D3 //運算符=進行y的淺拷貝 return 0; }
結果:
Constructor Number:A1
Constructor Number:B0
Constructor Number:C2
Constructor Number:D3
Destructor Number:D3
Destructor Number:C2
Destructor Number:D3
Destructor Number:A1
例4:
#include<iostream> #include <cmath> using namespace std; class point{ int x,y; public: point(int xx=0,int yy=0){ x= xx; y =yy; cout<<"point的構造函數被調用\n"; } point (point &p){ x = p.x; y = p.y; cout<<"point的複製構造函數被調用\n"; } int GetX(){ return x;} int GetY(){ return y;} }; class line{ point p1, p2; double len; public: line (point xp1,point xp2):p1(xp1),p2(xp2){ cout<<"line的構造函數被調用\n"; double x = double (p2.GetX()-p1.GetX()); double y = double (p2.GetY()-p1.GetY()); len = sqrt(x*x+y*y); //在私有數據中被定義,所以類外(class,不是對象)對len進行運算時,要用成員函數調用 } line (line &l); double GetLen(){ return len;} }; int main(){ point myp1(1,1),myp2(4,5); //myp1 兩個參數的構造函數 myp2兩個參數的構造函數 line line0(myp1,myp2); //myp1 做實參傳給line0的複製構造函數line 中的xp1時,xp1 通過複製構造函數得到myp1的值 //xp1 做實參傳給p1的複製構造函數point中的參數p時,p通過複製構造函數得到xp1的值 //myp2 xp2 p2 的類似上述過程,所以這邊一共調用了4次複製構造函數 cout<<"The length of the line0 is :"; cout<<line0.GetLen()<<endl; //計算 return 0; }
結果:
point的構造函數被調用
point的構造函數被調用
point的複製構造函數被調用
point的複製構造函數被調用
point的複製構造函數被調用
point的複製構造函數被調用
line的構造函數被調用
The length of the line0 is :5
例5:
#include<iostream> using namespace std; class Number{ int i; public: Number(int x=0){ i=x; cout<<"Constructor Number:"<<i<<endl; } ~Number(){ cout<<"Destructor Number:"<<i<<endl; } Number operator +(const Number &x){ Number result; cout<<"Add Number"<<endl; result.i = i+x.i; return result; } }; int main(){ Number x(1),y(2); //構造函數x(1)和構造函數y(2) y=x+y; //運算符+重載中默認值構造函數 //析構運算符+中的對象 //運算符=淺拷貝 //析構y 和 x return 0; }
結果:
Constructor Number:1
Constructor Number:2
Constructor Number:0
Add Number
Destructor Number:3
Destructor Number:3
Destructor Number:1
例6:構造函數的作用是初始化,構造初始化列表先於構造函數函數體執行。
#include <iostream> using namespace std; class data{ int x; public: data(int x){ data::x=x; cout<<"class data"<<endl; } }; class a{ data d1; public: a(int x):d1(x){ cout<<"class a"<<endl; } }; class b:public a{ data d2; public: b(int x):a(x),d2(x){ cout<<"class b"<<endl; } }; class c:public b{ public: c(int x):b(x){ cout<<"class c"<<endl; } }; int main(){ c obj(5); return 0; }
結果:
class data
class a
class data
class b
class c
例7
#include <iostream> using namespace std; class Sample{ int x,y; public: Sample(int a=1,int b=1){ x=1,y=b; disp(); } Sample(Sample &s){ x=s.x; y=s.y; cout<<"in copy construct"<<endl; } Sample &operator=(Sample &s){ x=s.x; y=s.y; cout<<"in operator = "<<endl; return *this; } ~Sample(){ if(x==y){ cout<<"x==y"<<endl; }else{ cout<<"x!=y"<<endl; } } void disp(){ cout<<"x="<<x<<"y="<<y<<endl; } friend void ms(Sample s); }; void ms(Sample s){ s.x=s.y; } int main(){ Sample s1(2,3); //調用兩個參數的構造函數 Sample s2 = s1; //調用複製構造函數,利用s1構造s2 ms(s2); //調用複製構造函數,利用s2構造無名對象,參加後續函數運算的是無名構造對象 //完成ms函數後,無名構造對象的x和y同,但是s2的值不受改變 //析構無名變量,析構s2,析構s1 return 0; }
結果:
x=1y=3
in copy construct
in copy construct
x==y
x!=y
x!=y
例8:
#include <iostream> using namespace std; class A{ public: virtual void print(){ cout<<"A::print"<<endl; } }; class B:public A{ public: void print(){ cout<<"B::print"<<endl; } }; class C:public B{ public: void print(){ cout<<"C::print"<<endl; } }; void print(A a){ //參數的類型是A ,相對於int float 之類的 a.print(); } int main(){ C c; print(c); //c是C類型的,和A類型不符合,所以往C的父類中找相對應的函數 return 0; }
結果:
A::print