一、看代码写结果——c++静态成员和临时对象
- <span style="font-size:18px;">#include <iostream>
-
- using namespace std;
-
- class human
- {
- public:
- human()
- {
- human_num++;
- }
- static int human_num;
- ~human()
- {
- human_num--;
- print();
- }
- void print()
- {
- cout<<"human num is: "<<human_num<<endl;
- }
- };
-
- int human::human_num = 0;
-
- human f1(human x)
- {
- x.print();
- return x;
- }
-
- int main()
- {
- human h1;
- h1.print();
- human h2 = f1(h1);
- h2.print();
-
- return 0;
- }</span>
解析:
这个程序的human类有一个静态成员human_num,每执行一次普通的构造函数human_num加1,每指向一次析构函数,human_num减1。注意,在f1()函数中会使用默认的复制构造函数,而默认的复制构造函数没有对human_num处理。
代码第34行,只构造了对象h1(调用普通构造函数),因此打印1。
代码第35行,使用值传递参数的方式调用了f1()函数,这里分为3步;
(1)在f1函数内首先会调用复制构造函数生成一个临时对象,因此代码第27行打印1。
(2)f1函数内调用复制构造函数,给main的对象h2初始化(复制临时对象)
(3)f1函数返回后,临时对象发生析构,此时human的静态成员human为0,打印出0。
代码第36行打印的还是0。
main函数结束时有h1和h2两个对象要发生析构,所以分别打印出-1和-2。
程序的意图很明显,就是静态成员用human_num记录类human的实例数。然而,由于默认的复制构造函数没有对静态成员操作,导致了执行结果的不正确。这里可以通过添加一个自定义的复制构造函数解决。
- human(human& h)
- {
- human_num++;
- }
此时human_num就能起到应有的作用了。
执行结果:
- human num is: 1
- human num is: 1
- human num is: 0
- human num is: 0
- human num is: -1
- human num is: -2
二、什么是临时对象?临时对象在什么情况下产生?
当程序员之间进行交谈时,经常把仅仅需要一小段时间的变量称为临时变量。例如在下面的swap函数里:
- void swap(int &a, int &b)
- {
- int temp = a;
- a = b;
- b = temp;
- }
通常称temp为临时变量。但在c++里,temp根本不是临时变量。实际上,他只是一个函数的局部变量。
真正的临时对象时看不见的,他不会出现在程序代码中。大多数情况下,他会影响程序执行的效率,所以有时想避免临时对象的产生。它通常在一下两种情况下产生。
(1)参数按值传递。
(2)返回值按值传递。
参考下面代码:
- #include <iostream>
-
- using namespace std;
-
- class Test
- {
- int num;
- public:
- Test() : num(0) { }
- Test(int number) : num(number) { }
- void print()
- {
- cout<<"num = "<<num<<endl;
- }
- ~Test()
- {
- cout<<"destructor: this = "<<this<<",num = "<<num<<endl;
- }
- };
-
- Test fun1(Test test)
- {
- test.print();
- }
-
- void fun2()
- {
- Test t(3);
- return t;
- }
-
- int main()
- {
- Test t1(1);
- fun1(t1);
- fun1(2);
- t1 = fun2();
-
- return 0;
- }
程序中fun1()参数是按值传递的,fun2函数的返回值是按值传递的。在Test类的析构函数中打印了this指针的值,下面是执行结果:
- num = 1
- destructor: this = 0xbffb2b84,num = 1 (fun1内的临时对象析构)
- num = 2
- destructor: this = 0xbffb2b88,num = 2 (fun1内的临时对象析构)
- destructor: this = 0xbffb2b8c,num = 3 (fun2内返回的临时对象析构)
- destructor: this = 0xbffb2b80,num = 3 (main内的t1析构)
这里代码第35,36行使用了两种类型参数传入fun1函数。它们都会生成临时变量,不同点是采用不同的方式:第35行的调用使用了复制构造函数创建临时变量,而第36行调用了带参数的构造函数创建临时变量。
如何避免临时变量的产生呢?可以使用引用传递代替值传递。这样就不会产生临时对象了。