美文网首页C++
C++:再论构造函数分类和调用时机以及匿名对象

C++:再论构造函数分类和调用时机以及匿名对象

作者: 重庆八怪 | 来源:发表于2021-02-17 17:12 被阅读0次

    原创请注明出处:


    我们这里主要讨论构造函数的构造函数的分类和其调用时机
    测试类如下:

    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();
    其他:=等号操作符重载使用的时机是(拷贝赋值)

    test e;
    e=b;

    如果是test e=b则是COPY构造函数。


    总结:实际上构造函数调用一定要分清是否是初始化和等号操作,等号操作调用的是操作符重载,而初始化是要考虑到内存空间的分配的,我们的构造行数都是在初始化的时候调用的。
    关于匿名对象需要考虑到内存分配,比如作为实参C++编译器自然要生成一个匿名对象在函数生命周期中使用,完成后自然要析构掉,如果作为一个返回值,自然也要生成一个匿名
    对象,关键是看你如何去接,如果初始化方法去接这个匿名对象的内存将会被"转正",如果不接受或者不是初始化则析构掉,因为没有用了。

    下面是全部的测试代码以及注释

    
    /*************************************************************************
      > File Name: test.cpp
      > Author: gaopeng QQ:22389860 all right reserved
      > Mail: gaopp_200217@163.com
      > Created Time: Fri 24 Mar 2017 08:19:28 PM CST
     ************************************************************************/
    
    #include<iostream>
    using namespace std;
    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<<" "<<&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)
                       {
    // cout<<"2个匿名对象生成,看如何接"<<endl;
                               p.plus20();
                               return p;
                       }
    //test new
               test& testy(test p) //返回为局部的匿名对象的引用,不能做左值不能用于初始化引用
                       {
                               p.plus20();
                               return p;
                       }
    
                       test& testm()//返回是引用内存空间永久可以作为左值
                       {
                               static test yy; 
                               yy.print();
                               return yy;
                       }
    
                    private:
                            int a;
                            int b;
    
            };
    }
    
    //
    int main()
    {
            using namespace test_n;
            test a; //1-1无参数构造函数调用test() 
            a.print();
            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();
    
            test e = b;//3-1copy构造函数调用test(const test &m),用b对象来初始化e
            e.print();
    
            test f(d);
            f.print();//3-2copy构造函数调用test(const test &m),用d对象来初始化f
    
            f.printany(d);//3-3copy构造函数调用test(const test &m),用d对象来初始化一个匿名对象,这个匿名对象在printany函数生命周期中使用,然后析构掉
    
            //f.printanys(d);**如果调用为引用当然不需要匿名对象建立了,因为这是传引用
            //f.printanys(&d);**如果调用为指针也不需要匿名对象的建立,因为传入是指针
    
            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();
    
    }
    
    //以下是函数返回引用作为左值和右值的测试
    //--函数返回用用分为以下
    //--返回为局部变量
    //可以用int a = test();来接
    //不可以用int &a = test();来接
    //不能做左值
    //
    //--返回为全局或者静态变量
    //可以用int a = test();来接
    //可以用int &a = test();来接
    //可以当左值
    //
    //a=test() 1  变量来接
    //int &a=test() 2 初始引用来接
    //test()=b  3 作为左值
    //int& test()
    //{
    // static int b ;
    // return b;
    //}
    int main12()
    {
            using namespace test_n;
            test b(1,2);//2-1有参构造函数调用test(int c_a,int c_b)
    
    // test a = b.testy(b);
    // test &c = b.testy(b);
    
            test m ;
            m = b.testy(b);
            m.print();
            m.testm() = b;//左值必须为static,及内存要永久
            m.testm();
    // a.print();
    // c.print();
    }
    
    
    运行结果:
    无参构造函数调用 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
    

    相关文章

      网友评论

        本文标题:C++:再论构造函数分类和调用时机以及匿名对象

        本文链接:https://www.haomeiwen.com/subject/kuaixltx.html