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

原创请注明出处:

我们这里主要讨论构造函数的构造函数的分类和其调用时机
测试类如下:
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;

时间: 2024-08-04 04:30:10

再论C++构造函数分类和调用时机以及匿名对象的相关文章

浅谈C++中的构造函数分类及调用规则_C 语言

构造函数的分类这里简单地将C++中的构造函数分一下类,直接看下面的代码表达,说明在注释中: #include <iostream> using namespace std; class Text { public: Text() // 无参数构造函数 { m_a = 0; m_b = 0; cout << "无参数构造函数" << endl; } Text(int a) // 有参数构造函数 { m_a = a; m_b = 0; cout <

拷贝构造函数的调用时机

[拷贝构造函数的调用时机] 1.对象以值传递的方式传入函数参数 2.对象以值传递的方式从函数返回 3.对象需要通过另外一个对象进行初始化    拷贝构造函数原型,假设类名为Foo,则copy constructor为: Foo( const Foo &obj); 限定符必须是const,且必须是引用.

MFC浅析(7) CWnd类虚函数的调用时机、缺省实现 .

1. Create 2. PreCreateWindow 3. PreSubclassWindow 4. PreTranslateMessage 5. WindowProc 6. OnCommand 7. OnNotify 8. OnChildNotify 9. DefWindowProc 10. DestroyWindow 11. PostNcDestroy CWnd作为MFC中最基本的与窗口打交道的类,完成了大部分窗口管理任务.同时提供了很多虚拟函数,这些虚拟函数在适当的地方提供了供派生类参

匿名对象赋值-大神帮看一下代码,为什么t5 = addC(a1, a2)中,只调用了一次copy构造函数?

问题描述 大神帮看一下代码,为什么t5 = addC(a1, a2)中,只调用了一次copy构造函数? #include<iostream> using namespace std; class ABC { public: ABC(int a = 0, int b = 0) { this->a = a; this->b = b; printf("我是有参构造函数!n"); } ABC(ABC &c) { this->a = c.a; this-&g

asp net mvc-asp.net mvc中有参构造函数在哪里调用的

问题描述 asp.net mvc中有参构造函数在哪里调用的 又是如果指定调用哪个构造函数的(默认无参数的构造函数和自己定义的有参数的构造函数)

控件的构造函数是怎么调用的?

问题描述 控件的构造函数是怎么调用的? 一般来说控件都是通过 findviewbyId 去实例化,他的构造函数是怎么调用的 解决方案 构造函数是android系统去调用的,你可以写构造函数,在里面加上一点调试输出,就可以看到它被调用了. 解决方案二: 好像直接new就可以了

java-Java中为什么匿名对象中方法,调用局部变量必须加final呢,求详解

问题描述 Java中为什么匿名对象中方法,调用局部变量必须加final呢,求详解 Java中为什么匿名对象中方法,调用局部变量必须加final呢,求详解.在网上找了很久,基本没看到想要的答案,最好从Java虚拟机内存分配角度讲一讲,其他角度亦可. 老师只告诉这么用就好了,问了也说不知道,匿名对象在哪没研究过 求大神回复 解决方案 为什么匿名内部类调用的方法内局部变量必须为final为什么匿名内部类调用的方法内局部变量必须为final为什么匿名内部类调用的方法内局部变量必须为final 解决方案二

线程-java中下面这段代码是一个匿名对象传入一个匿名对象在调用start方法吗

问题描述 java中下面这段代码是一个匿名对象传入一个匿名对象在调用start方法吗 new Thread(new Runnable() {.....省略代码若干..........}).start(); 上面这段代码是一个匿名对象传入一个匿名对象在调用start方法吗 还是一个匿名类传入一个匿名对象再调用start方法 我有点分不清匿名对象和匿名类呢 解决方案 new Runnable() {.....省略代码若干..........} 匿名类,因为Runnable是接口,需要实现类 new

实现整个网站系统只有一个connection对象,可是对象只能使用一次,再次调用时提示:connection对象的connectionstring属性未初始化?内含代码

问题描述 实现整个网站系统只有一个connection对象,可是对象只能使用一次,再次调用时提示:connection对象的connectionstring属性未初始化???????..................为什么????????创建connection对象publicclassPubConn{privatePubConn(){}privatestaticOracleConnectionDALPubConnection;publicstaticOracleConnectionPubCo