一、看代碼寫結果——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行調用了帶參數的構造函數建立臨時變量。
如何避免臨時變量的産生呢?可以使用引用傳遞代替值傳遞。這樣就不會産生臨時對象了。