再論C++建構函式分類和呼叫時機以及匿名物件

gaopengtttt發表於2017-03-05
原創請註明出處:

我們這裡主要討論建構函式的建構函式的分類和其呼叫時機
測試類如下:
namespace test_n
{
        int A_G=0;
        class test
        {
                public:
                        test() //無引數建構函式,如果沒有呼叫預設建構函式
                        {
                                cout<<"無參建構函式呼叫"<<" adress: "<<&a<<endl;
                        }
                        test(int c_a,int c_b)//有參建構函式
                        {
                                cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
                                a=c_a;
                                b=c_b;


                        }
                        test(int c_a)
                        {
                                cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
                                a=c_a;
                        }


                        test(const test &m) //copy 建構函式
                        {
                                cout<<"copy建構函式呼叫"<<" adress: "<<&a<<endl;
                                a = m.a;
                                b = m.b;
                        }


                        void print()
                        {
                                cout<<a<<" "<<b<<endl;=""  ="" }=""
                        ~test()
                        {
                                cout<<"解構函式呼叫"<<" adress: "<<&a<<endl;
                                A_G++;
                                cout<<a_g<<endl;
                        }
                        void plus20()
                        {
                                a = a+20;
                                b = b+20;
                        }
//3-3
                        void printany(test p)
                        {
                                cout<<"匿名物件生成"<<endl;
                                p.print();
                        }
                        void printany(test* p)
                        {
                                cout<<"無名物件生成"<<endl;
                                p->print();
                        }
                        void printanys(test& p)
                        {
                                cout<<"無名物件生成"<<endl;
                                p.print();
                        }


//3-4
                   test test34(test p)
                   {
                           p.plus20();
                           return p;
                   }
                private:
                        int a;
                        int b;


        };
}




1、預設建構函式和無參建構函式
實際上預設建構函式就是什麼都不做的無參建構函式,這個時候如果不初始化會私有變數的值會出現一些垃圾值
如:
test() //無引數建構函式
{
     cout<<"無參建構函式呼叫"<<" adress: "<<&a<<endl;
}
這樣成員變數a和b就會出現垃圾值


呼叫時機:
test a; //1-1無引數建構函式呼叫test(),如果沒有呼叫預設建構函式 


2、有參建構函式
如下都是有參建構函式:
test(int c_a,int c_b)//有參建構函式
{
        cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
        a=c_a;
        b=c_b;


}
test(int c_a)//有參建構函式
{      
        cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
        a=c_a;
}


呼叫時機:
test b(1,2);//2-1有參建構函式呼叫test(int c_a,int c_b)
b.print();
test c = (100,100);//2-2有參建構函式呼叫100,100後面的這一個100忽略,逗號表示式,呼叫test(int c_a)
c.print();
test d = test(100,100);//2-3有參建構函式呼叫test(int c_a,int c_b),先生成一個匿名物件 然後被賦予給d,只呼叫一次構造和解構函式
d.print();


3、COPY建構函式(也叫賦值建構函式)
如下:
test(const test &m) //copy 建構函式
{
        cout<<"copy建構函式呼叫"<<" adress: "<<&a<<endl;
        a = m.a;
        b = m.b;
}
copy建構函式呼叫時機有4種其中有2個難點就是,將物件作為實參和物件作為返回值的時候
呼叫時機:
3-1:用其他物件初始化賦值操作
        test e = b;//3-1copy建構函式呼叫test(const test &m),用b物件來初始化e 注意是初始化而不是等號操作
        e.print();
3-2:使用COPY建構函式原型,實參為其他物件
        test f(d);
        f.print();//3-2copy建構函式呼叫test(const test &m),用d物件來初始化f
3-3:難點作為函式實參,會生成匿名物件
        f.printany(d);//3-3copy建構函式呼叫test(const test &m),用d物件來初始化一個匿名物件,這個匿名物件在printany函式生命週期中使用,然後析構掉
3-4:難點作為函式返回值,會生成匿名物件,當作為返回值的時候匿名物件又要考慮到如何去接這個返回值分3種如下
        d.test34(d);//3-4 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,最後呼叫函式無接就析構掉

        test m = d.test34(d);//3-5 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,最後用於初始化
,因為匿名物件記憶體已經分配,就直接用匿名物件的記憶體給物件m即可,它的析構隨m的使用完成而析構,避免不必要的記憶體分配和析構提高效能
        m.print();
        c = d.test34(d); //3-6 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,雖然有接,但是這個
使用等於符號過載操作,因為c已經有了記憶體空間,那麼也會及時析構掉
        c.print();


總結:實際上建構函式呼叫一定要分清是否是初始化和等號操作,等號操作呼叫的是操作符過載,而初始化是要考慮到記憶體空間的分配的,我們的構造行數都是在初始化的時候呼叫的。
     關於匿名物件需要考慮到記憶體分配,比如作為實參C++編譯器自然要生成一個匿名物件在函式生命週期中使用,完成後自然要析構掉,如果作為一個返回值,自然也要生成一個匿名
     物件,關鍵是看你如何去接,如果初始化方法去接這個匿名物件的記憶體將會被"轉正",如果不接受或者不是初始化則析構掉,因為沒有用了。
     
下面是全部的測試程式碼以及註釋

點選(此處)摺疊或開啟

  1. /*************************************************************************
  2.   > File Name: test.cpp
  3.   > Author: gaopeng QQ:22389860 all right reserved
  4.   > Mail: gaopp_200217@163.com
  5.   > Created Time: Fri 24 Mar 2017 08:19:28 PM CST
  6.  ************************************************************************/

  7. #include<iostream>
  8. using namespace std;
  9. namespace test_n
  10. {
  11.         int A_G=0;
  12.         class test
  13.         {
  14.                 public:
  15.                         test() //無引數建構函式,如果沒有呼叫預設建構函式
  16.                         {
  17.                                 cout<<"無參建構函式呼叫"<<" adress: "<<&a<<endl;
  18.                         }
  19.                         test(int c_a,int c_b)//有參建構函式
  20.                         {
  21.                                 cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
  22.                                 a=c_a;
  23.                                 b=c_b;

  24.                         }
  25.                         test(int c_a)
  26.                         {
  27.                                 cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
  28.                                 a=c_a;
  29.                         }

  30.                         test(const test &m) //copy 建構函式
  31.                         {
  32.                                 cout<<"copy建構函式呼叫"<<" adress: "<<&a<<endl;
  33.                                 a = m.a;
  34.                                 b = m.b;
  35.                         }

  36.                         void print()
  37.                         {
  38.                                 cout<<a<<" "<<&a<<" "<<b<<endl;
  39.                         }

  40.                         ~test()
  41.                         {
  42.                                 cout<<"解構函式呼叫"<<" adress: "<<&a<<endl;
  43.                                 A_G++;
  44.                                 cout<<A_G<<endl;
  45.                         }
  46.                         void plus20()
  47.                         {
  48.                                 a = a+20;
  49.                                 b = b+20;
  50.                         }
  51. //3-3
  52.                         void printany(test p)
  53.                         {
  54.                                 cout<<"匿名物件生成"<<endl;
  55.                                 p.print();
  56.                         }
  57.                         void printany(test* p)
  58.                         {
  59.                                 cout<<"無匿名物件生成"<<endl;
  60.                                 p->print();
  61.                         }
  62.                         void printanys(test& p)
  63.                         {
  64.                                 cout<<"無匿名物件生成"<<endl;
  65.                                 p.print();
  66.                         }

  67. //3-4
  68.                    test test34(test p)
  69.                    {
  70. // cout<<"2個匿名物件生成,看如何接"<<endl;
  71.                            p.plus20();
  72.                            return p;
  73.                    }
  74. //test new
  75.            test& testy(test p) //返回為區域性的匿名物件的引用,不能做左值不能用於初始化引用
  76.                    {
  77.                            p.plus20();
  78.                            return p;
  79.                    }

  80.                    test& testm()//返回是引用記憶體空間永久可以作為左值
  81.                    {
  82.                            static test yy;
  83.                            yy.print();
  84.                            return yy;
  85.                    }

  86.                 private:
  87.                         int a;
  88.                         int b;

  89.         };
  90. }

  91. //
  92. int main()
  93. {
  94.         using namespace test_n;
  95.         test a; //1-1無引數建構函式呼叫test()
  96.         a.print();
  97.         test b(1,2);//2-1有參建構函式呼叫test(int c_a,int c_b)
  98.         b.print();
  99.         test c = (100,100);//2-2有參建構函式呼叫100,100後面的這一個100忽略,呼叫test(int c_a)
  100.         c.print();
  101.         test d = test(100,100);//2-3有參建構函式呼叫test(int c_a,int c_b),先生成一個匿名物件 然後被賦予給d,只呼叫一次構造和解構函式
  102.         d.print();

  103.         test e = b;//3-1copy建構函式呼叫test(const test &m),用b物件來初始化e
  104.         e.print();

  105.         test f(d);
  106.         f.print();//3-2copy建構函式呼叫test(const test &m),用d物件來初始化f

  107.         f.printany(d);//3-3copy建構函式呼叫test(const test &m),用d物件來初始化一個匿名物件,這個匿名物件在printany函式生命週期中使用,然後析構掉

  108.         //f.printanys(d);**如果呼叫為引用當然不需要匿名物件建立了,因為這是傳引用
  109.         //f.printanys(&d);**如果呼叫為指標也不需要匿名物件的建立,因為傳入是指標

  110.         d.test34(d);//3-4 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,最後呼叫函式無接就析構掉

  111.         test m = d.test34(d);//3-5 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,最後用於初始化
  112. ,因為匿名物件記憶體已經分配,就直接用匿名物件的記憶體給物件m即可,它的析構隨m的使用完成而析構,避免不必要的記憶體分配和析構提高效能
  113.         m.print();

  114.         c = d.test34(d); //3-6 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,雖然有接,但是這個
  115. 使用等於符號過載操作,因為c已經有了記憶體空間,那麼也會及時析構掉
  116.         c.print();

  117. }

  118. //以下是函式返回引用作為左值和右值的測試
  119. //--函式返回用用分為以下
  120. //--返回為區域性變數
  121. //可以用int a = test();來接
  122. //不可以用int &a = test();來接
  123. //不能做左值
  124. //
  125. //--返回為全域性或者靜態變數
  126. //可以用int a = test();來接
  127. //可以用int &a = test();來接
  128. //可以當左值
  129. //
  130. //a=test() 1 變數來接
  131. //int &a=test() 2 初始引用來接
  132. //test()=b 3 作為左值
  133. //int& test()
  134. //{
  135. // static int b ;
  136. // return b;
  137. //}
  138. int main12()
  139. {
  140.         using namespace test_n;
  141.         test b(1,2);//2-1有參建構函式呼叫test(int c_a,int c_b)

  142. // test a = b.testy(b);
  143. // test &c = b.testy(b);

  144.         test m ;
  145.         m = b.testy(b);
  146.         m.print();
  147.         m.testm() = b;//左值必須為static,及記憶體要永久
  148.         m.testm();
  149. // a.print();
  150. // c.print();
  151. }


執行結果:
無參建構函式呼叫 adress: 0x7ffc20853490
1771925424 32660
有參建構函式呼叫 adress: 0x7ffc208534a0
1 2
有參建構函式呼叫 adress: 0x7ffc208534b0
100 0
有參建構函式呼叫 adress: 0x7ffc208534c0
100 100
copy建構函式呼叫 adress: 0x7ffc208534d0
1 2
copy建構函式呼叫 adress: 0x7ffc208534e0
100 100
copy建構函式呼叫 adress: 0x7ffc208534f0
匿名物件生成
100 100
解構函式呼叫 adress: 0x7ffc208534f0
1
copy建構函式呼叫 adress: 0x7ffc20853500
copy建構函式呼叫 adress: 0x7ffc20853510
解構函式呼叫 adress: 0x7ffc20853510
2
解構函式呼叫 adress: 0x7ffc20853500
3
copy建構函式呼叫 adress: 0x7ffc20853530
copy建構函式呼叫 adress: 0x7ffc20853520
解構函式呼叫 adress: 0x7ffc20853530
4
120 120
copy建構函式呼叫 adress: 0x7ffc20853540
copy建構函式呼叫 adress: 0x7ffc20853550
解構函式呼叫 adress: 0x7ffc20853550
5
解構函式呼叫 adress: 0x7ffc20853540
6
120 120
解構函式呼叫 adress: 0x7ffc20853520
7
解構函式呼叫 adress: 0x7ffc208534e0
8
解構函式呼叫 adress: 0x7ffc208534d0
9
解構函式呼叫 adress: 0x7ffc208534c0
10
解構函式呼叫 adress: 0x7ffc208534b0
11
解構函式呼叫 adress: 0x7ffc208534a0
12
解構函式呼叫 adress: 0x7ffc20853490
13



作者微信:

               
</endl;
</endl;
</endl;
</endl;
</endl;
</endl;
</endl;
</a_g<<endl;
</endl;
</a<</endl;
</endl;
</endl;
</endl;

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2134726/,如需轉載,請註明出處,否則將追究法律責任。

相關文章