我想问下这2段程序有什么不一样?
如题,有木有大神能和我解答下构造器和析构器的不同,如下2段程序的运行方式有什么区别?然后输出为什么是这样的呢?谢谢class Zebra {
private:
int age;
static int count;
public:
Zebra() {
age = 0;
count++;
}
~Zebra() {
count--;
}
static int getCount() { return count; }
};
int Zebra::count = 0;
int main() {
Zebra z1;
Zebra z2;
cout << Zebra::getCount() << "\n";
} 第二段代码如下:
class Zebra {
private:
int age;
static int count;
public:
Zebra() {
age = 0;
count++;
}
~Zebra() {
count--;
}
static int getCount() { return count; }
};
int Zebra::count = 0;
void f(Zebra z) {
cout << Zebra::getCount();
}
int main() {
Zebra z1;
f(z1);
Zebra z2;
cout << Zebra::getCount();
}
本帖最后由 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。
综上,第二段代码运行结果是 11。
要想正确计数,可以自己添加一个复制构造函数,如下:
Zebra(Zebra &z){
age=z.age;
count++;
}
添加后运行结果是 2 2。也就是z1加上临时对象共2个对象,然后函数退出销毁一个再创建一个z2后,还是2个。
PS:执行构造函数和析构函数都有几种情况,最基本的是创建对象时调用构造函数,对象生命周期结束时销毁对象调用析构函数。 superbe 发表于 2019-9-27 15:38
第一段代码比较简单,静态成员count是记录Zebra对象的数量的,main定义了两个Zebra对象,z1和z2,都会调用 ...
我想问一下,第一段代码再执行完构造器后会执行析构器里面的内容吗?
还有第二段代码你给我的解释第二部分我有点不明白,你可以稍微解释下复制构造函数吗?
还有老师给的那个很麻烦的作业题我今晚就上传给你呢,谢谢,我要先把那个题理解了然后先做一遍在发呢! 本帖最后由 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 函数时就能正确计数了。
啰啰嗦嗦説了一大堆,不知道表达清楚了没有。 superbe 发表于 2019-10-1 14:38
1. 析构器不是在执行构造器后就调用。main函数里的z1和z2,在main结束时它们的生命周期结束,需要销毁z1和z ...
懂啦!!谢谢!
我们老师什么都没说,就让我们写这个作业,哎。我在抓紧看视频,但是感觉也没学多少。。
对了,麻烦你有时间看下我的那个julianday的问题呢,那个困扰了我好久-。-谢谢呢
页:
[1]