鱼C论坛

 找回密码
 立即注册
查看: 1575|回复: 5

[已解决]我想问下这2段程序有什么不一样?

[复制链接]
发表于 2019-9-27 11:56:55 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
如题,有木有大神能和我解答下构造器和析构器的不同,如下2段程序的运行方式有什么区别?然后输出为什么是这样的呢?谢谢
  1. class Zebra {
  2.    
  3. private:
  4.     int age;
  5.     static int count;
  6. public:
  7.     Zebra() {
  8.         age = 0;
  9.         count++;
  10.     }
  11.     ~Zebra() {
  12.         count--;
  13.     }
  14.     static int getCount() { return count; }
  15. };

  16. int Zebra::count = 0;

  17. int main() {
  18.             Zebra z1;
  19.             Zebra z2;
  20.             cout << Zebra::getCount() << "\n";
  21. }
复制代码
最佳答案
2019-9-27 15:38:24
本帖最后由 superbe 于 2019-9-27 17:29 编辑

第一段代码比较简单,静态成员count是记录Zebra对象的数量的,main定义了两个Zebra对象,z1和z2,都会调用默认构造函数Zebra()执行count++,运行结果是2,也就是有2个Zebra对象。

第二段代码:
第1步:创建z1,执行默认构造函数Zebra()中count++,这时count=1。
第2步:调用函数f(z1),函数 f 会为形参创建一个临时Zebra对象,退出函数时再销毁这个对象。并且会调用复制构造函数初始化临时对象,而不调用默认构造函数Zebra()。由于Zerbra没有定义复制构造函数,编译器会生成一个“默认复制构造函数”,它的功能只是简单地将把z1成员的值赋给临时对象成员,而不会操作静态成员,所以count的值仍然是1,临时对象没有被计数。
第3步:退出f函数,销毁临时对象,这时执行析构函数使count--,count变成了0。
第4步:创建z2,执行默认构造函数Zebra()中count++,这时count=1。
综上,第二段代码运行结果是 1  1
要想正确计数,可以自己添加一个复制构造函数,如下:
Zebra(Zebra &z){
        age=z.age;
        count++;
}
添加后运行结果是 2   2。也就是z1加上临时对象共2个对象,然后函数退出销毁一个再创建一个z2后,还是2个。


PS:执行构造函数和析构函数都有几种情况,最基本的是创建对象时调用构造函数,对象生命周期结束时销毁对象调用析构函数。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2019-9-27 11:57:39 | 显示全部楼层
第二段代码如下:
  1. class Zebra {
  2. private:
  3.   int age;
  4.   static int count;
  5. public:
  6.   Zebra() {
  7.     age = 0;
  8.     count++;
  9.   }
  10.   ~Zebra() {
  11.     count--;
  12.   }
  13.   static int getCount() { return count; }
  14. };

  15. int Zebra::count = 0;

  16. void f(Zebra z) {
  17.   cout << Zebra::getCount();
  18. }

  19. int main() {
  20.   Zebra z1;
  21.   f(z1);
  22.   Zebra z2;
  23.   cout << Zebra::getCount();
  24. }

复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-9-27 15:38:24 | 显示全部楼层    本楼为最佳答案   
本帖最后由 superbe 于 2019-9-27 17:29 编辑

第一段代码比较简单,静态成员count是记录Zebra对象的数量的,main定义了两个Zebra对象,z1和z2,都会调用默认构造函数Zebra()执行count++,运行结果是2,也就是有2个Zebra对象。

第二段代码:
第1步:创建z1,执行默认构造函数Zebra()中count++,这时count=1。
第2步:调用函数f(z1),函数 f 会为形参创建一个临时Zebra对象,退出函数时再销毁这个对象。并且会调用复制构造函数初始化临时对象,而不调用默认构造函数Zebra()。由于Zerbra没有定义复制构造函数,编译器会生成一个“默认复制构造函数”,它的功能只是简单地将把z1成员的值赋给临时对象成员,而不会操作静态成员,所以count的值仍然是1,临时对象没有被计数。
第3步:退出f函数,销毁临时对象,这时执行析构函数使count--,count变成了0。
第4步:创建z2,执行默认构造函数Zebra()中count++,这时count=1。
综上,第二段代码运行结果是 1  1
要想正确计数,可以自己添加一个复制构造函数,如下:
Zebra(Zebra &z){
        age=z.age;
        count++;
}
添加后运行结果是 2   2。也就是z1加上临时对象共2个对象,然后函数退出销毁一个再创建一个z2后,还是2个。


PS:执行构造函数和析构函数都有几种情况,最基本的是创建对象时调用构造函数,对象生命周期结束时销毁对象调用析构函数。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-30 22:44:52 | 显示全部楼层
superbe 发表于 2019-9-27 15:38
第一段代码比较简单,静态成员count是记录Zebra对象的数量的,main定义了两个Zebra对象,z1和z2,都会调用 ...


我想问一下,第一段代码再执行完构造器后会执行析构器里面的内容吗?
还有第二段代码你给我的解释第二部分我有点不明白,你可以稍微解释下复制构造函数吗?
还有老师给的那个很麻烦的作业题我今晚就上传给你呢,谢谢,我要先把那个题理解了然后先做一遍在发呢!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-10-1 14:38:19 | 显示全部楼层
本帖最后由 superbe 于 2019-10-1 14:42 编辑

1. 析构器不是在执行构造器后就调用。main函数里的z1和z2,在main结束时它们的生命周期结束,需要销毁z1和z2,才会调用析构器。同样,f 函数里的形参变量在 f 结束时它的生命周期结束,需要销毁才会调用析构器。

2. 第二部分的意思:

(1) f 函数的参数是一个Zebra对象,对象做为参数时也象普通变量那样分“按值”和“按引用”两种情况。f 的参数 z 前没有&符号就说明它是“按值传递”的情况。还记得吧,这种情况下,实际参数的值会复制给形式参数,两者实际是两个不同的变量。

(2) 那么实际参数是如何复制给形式参数的呢,普通变量比如一个int,直接把int值给形参变量就可以了。但是 f 的参数不是普通变量,而是一个对象,一个对象可能包括多个成员变量,所以复制时会将实参的每个成员变量一 一对应赋值给形参的成员变量,这就是“复制构造函数”干的事情。“复制构造函数”一般形式是这样的:
Zebra(Zebra &z){
        age=z.age;    //如果还有其它成员,每个成员都与此行类似,即 xxx = z.xxx ;
}
即用类名做为函数名,用该类的对象做参数(对象前加&表示按引用传递)。类中如果定义了这样的一个函数,调用 f(z1) 时就会自动执行这个函数,用 z1 的各个成员变量给形参对应成员赋值。

(3) 但是Zerbra类中并没有定义这样一个“复制构造函数”,那么调用 f(z1)时怎么给形参变量赋值呢?这时编译器“贴心”地为我们准备了一个“默认复制构造函数”(前面多了默认两个字),它完成的功能跟上面所説的一样,这样的话也就能正常调用 f 函数了。

(4) 既然编译器给我们准备了一个“默认复制构造函数”,那我们就再也不用自己定义“复制构造函数”了吧?不是的。像第二段代码这种情况,类中有一个静态成员变量count,目的是每增加一个对象就将这个静态变量加1 ,从而实现记录对象数量的目的。可以这么理解,类的静态成员是属于类的,不是属于某个具体对象的。而如上所説“默认复制构造函数”只是简单地复制对象成员变量的值(这里就是age),对于的静态成员count它完全不理会(当它不存在,更不会加1)。所以 f 虽然创建了一个Zerbra形参对象,但并没有被计数。

(5) 由于“默认复制构造函数”不能完成count加1的功能,这种情况下我们只能自己定义一个“复制构造函数”了,除了完成正常的成员变量赋值外,要在其中加入count加1的代码,如下:
Zebra(Zerbra &z){
        age=z.age;
        count++;    //这行是增加的
}
这样调用 f 函数时就能正确计数了。


啰啰嗦嗦説了一大堆,不知道表达清楚了没有。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-10-1 21:54:59 | 显示全部楼层
superbe 发表于 2019-10-1 14:38
1. 析构器不是在执行构造器后就调用。main函数里的z1和z2,在main结束时它们的生命周期结束,需要销毁z1和z ...

懂啦!!谢谢!
我们老师什么都没说,就让我们写这个作业,哎。我在抓紧看视频,但是感觉也没学多少。。
对了,麻烦你有时间看下我的那个julianday的问题呢,那个困扰了我好久-。-谢谢呢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-18 16:52

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表