1. 程式人生 > >聊聊C++臨時物件的析構時間點------順便再次提醒大家謹慎使用string的c_str方法

聊聊C++臨時物件的析構時間點------順便再次提醒大家謹慎使用string的c_str方法

       C++臨時物件的析構?這不是很簡單麼?其實沒那麼簡單。 我們先看看如下程式吧:

#include <iostream>
#include <string>
using namespace std;

string test()
{
	return "abc";
}

int main()
{
	const char *p = test().c_str();
	cout << p << endl;

	return 0;
}
        你可能迫切地期待結果為:abc, 其實不然。 為了簡便起見, 我們簡化一下上述程式:
#include <iostream>
#include <string>
using namespace std;

int main()
{
	const char *p = string("abc").c_str();
	cout << p << endl;

	return 0;
}
       注意, 結果不是abc. 為什麼呢? 我先直接說吧:string("abc")是一個臨時物件, 在執行完const char *p = string("abc").c_str();這個語句後, 臨時物件就析構了, 也就是說p指向的區域中的值變了。 所以, 結果自然不是abc.

       這就引出瞭如下問題: 臨時物件是何時析構的呢? 我們先看一個程式:

#include <iostream>
#include <string>
using namespace std;

class A
{
public:
	A()
	{
		cout << "A constructor" << endl;
	}

	~A()
	{
		cout << "A destructor" << endl;
	}
};

int main()
{
	A(); // 臨時物件

	printf("end xxx\n");
	printf("end yyy\n");

	return 0;
}
      稍微懂一點C++的人會說, 結果是:

A constructor
end xxx
end yyy
A destructor

      其實, 上述結果是錯誤的, 真正的結果是:

A constructor
A destructor
end xxx
end yyy

       看來, 在執行完第一個語句後, 臨時物件A()就析構了, 我們來看看彙編, 驗證一下吧:


        我們看到, 臨時物件確實是在printf之前析構的。

        好, 我們接著看:

#include <iostream>
#include <string>
using namespace std;

class A
{
public:
	A()
	{
		cout << "A constructor" << endl;
	}

	~A()
	{
		cout << "A destructor" << endl;
	}
};

int main()
{
	A(), // 注意, 是逗號運算子
	printf("end xxx\n");
	printf("end yyy\n");

	return 0;
}
      執行結果為:

A constructor
end xxx
A destructor
end yyy


      不要驚訝, 檢視彙編程式碼就知道, 臨時物件是在printf("end xxx\n");後析構的。

      繼續看程式碼:

#include <iostream>
#include <string>
using namespace std;

class A
{
public:
	A()
	{
		cout << "A constructor" << endl;
	}

	~A()
	{
		cout << "A destructor" << endl;
	}
};

int main()
{
	A(), // 注意, 是逗號運算子
	printf("end xxx\n"), // 注意, 是逗號運算子
	printf("end yyy\n");

	return 0;
}
      執行結果為:

A constructor
end xxx
end yyy
A destructor

       不要驚訝, 檢視彙編程式碼就知道, 臨時物件是在printf("end xxx\n");後析構的。

       由此可見, 臨時物件是在遇到其後的第一個分號(語句結束處)析構的。

        好, 我們再看看:

#include <iostream>
#include <string>
using namespace std;

int main()
{
	const char *p = string("abc").c_str(); // 臨時物件在執行完該句後析構了
	cout << p << endl; // 此時p指向垃圾值

	return 0;
}
       一切一目瞭然。

       大家在使用臨時物件的時候要留個心眼, 尤其是使用string的c_str時, 一旦出錯, 經常排查半天, 最後才發現栽倒在此處。 鑑於容易出錯, 最後, 我們再看一眼吧:

#include <iostream>
#include <string>
using namespace std;

int main()
{
	const char *p = (string("abc") + string("def")).c_str(); // 臨時物件在執行完該句後析構了
	cout << p << endl; // 此時p指向垃圾值

	return 0;
}

      OK,  本文先到此為止。

       備註: 在上面的程式中, 我使用的編譯器是VC++6.0, 後來網友“時光”提醒我, g++的編譯器會有不同的表現。 在此, 感謝“時光”得意。 另外, 為了寫出高質量的可移植程式碼, 仍需要注意避免使用臨時string物件的c_str方法。