一、看代码写结果——c++静态成员和临时对象
#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;
}
解析:
这个程序的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() //打印私有成员num
{
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); //整型数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行调用了带参数的构造函数创建临时变量。
如何避免临时变量的产生呢?可以使用引用传递代替值传递。这样就不会产生临时对象了。