構造,析構,複製構造,重載=,和自動創建無名變量的問題(習題)

關於構造,析構,複製構造,=,和自動創建無名變量

總結:

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








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章