美文网首页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++:再论构造函数分类和调用时机以及匿名对象

    原创请注明出处: 我们这里主要讨论构造函数的构造函数的分类和其调用时机测试类如下: 1、默认构造函数和无参构造函数...

  • JS 函数

    函数的定义 具名函数 匿名函数 箭头函数 构造函数 调用时机 先赋值后调用 先调用后赋值 setTimeout 作...

  • c++面向对象-构造、析构、初始化列表

    构造函数 构造函数是指创建对象的时候,会调用的函数。C++构造函数可以重载,指针或者对象均可调用构造函数。 析构函...

  • C++ 类和对象(三)(6)

    1. 多个对象的构造函数和析构函数 1.如果类存在成员对象,那么先调用成员对象的构造函数,再调用本身的构造函数,析...

  • [C++之旅] 10 构造函数

    [C++之旅] 10 构造函数 构造函数的特点 构造函数在对象实例化时被自动调用 构造函数与类同名 构造函数没有返...

  • C++之构造进阶之拷贝构造

    拷贝构造函数的概述 拷贝构造函数的本质是构造函数。 调用拷贝构造的时机:旧对象给新对象初始化。 用户不提供拷贝构造...

  • 在 JS 中,关于函数的执行时机

    函数是一种对象,函数是由 Function 构造的 函数的要素 每个函数都有默认的调用时机 函数的调用时机 && ...

  • 12-05构造析构

    构造。析构函数的出现时机 对象生成时会自动调用构造函数,但由于不同作用域的对象生命周期不同,构造函数出现的时机也不...

  • Python:重载构造方法

    对于使用过C++的人来说,构造函数与析构函数不会陌生。构造函数在对象创建时被调用,析构函数在对象被销毁时被调用。而...

  • JAVA 核心笔记 || [6] 构造函数

    构造函数 语法 类的构造函数调用时机: 用new 关键字创建对象时调用 类的构造函数必须与它所在的类名具有相同的名...

网友评论

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

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