- C++偶現問題備錄
- 1. 偶現問題原始碼
- 2. 問題根因分析
- 3. 修復問題原始碼
C++偶現問題備錄
1. 偶現問題原始碼
原始碼示例如下:
class KZNCalculationException : public std::exception
{
public:
#ifdef KZN_LINUX
KZNCalculationException(GString AMsg, GString AHit)
: std::exception()
{
m_sMsg = AMsg;
m_sHit = AHit;
}
#else
KZNCalculationException(GString AMsg, GString AHit)
: std::exception(AMsg.toStdString().c_str())
{
m_sHit = AHit;
}
#endif
GString getHit()
{
return m_sHit;
}
#ifdef KZN_LINUX
virtual const char* what() const throw ()
{
return m_sMsg.toStdString().c_str();
}
#endif
private:
GString m_sHit;
#ifdef KZN_LINUX
GString m_sMsg;
#endif
};
2. 問題根因分析
問題核心程式碼:m_sMsg.toStdString().c_str();
在Qt框架中,QString 是用於儲存和操作Unicode字串的類。當你需要將 QString 轉換為標準C++字串(即 std::string),以便與某些只接受C風格字串(const char*)的API或函式互動時,你可能會遇到 QString.toStdString().c_str() 這樣的表示式。下面我將詳細解釋這個表示式的含義和用法。
QString.toStdString()
QString.toStdString() 是 QString 類的一個成員函式,它返回一個 std::string 物件,該物件包含了與 QString 相同的字元序列,但編碼為UTF-8(或系統預設的本地編碼,這取決於Qt的配置和版本,但通常推薦和預設的是UTF-8)。這個轉換是必需的,因為 QString 內部使用Unicode編碼來儲存字串,而 std::string 通常用於儲存以特定編碼(如UTF-8)表示的位元組序列。
std::string.c_str()
std::string.c_str() 是 std::string 類的一個成員函式,它返回一個指向以null結尾的字元陣列(const char*)的指標,該陣列包含了字串的副本。這個指標指向的字串是臨時的,並且僅在呼叫 c_str() 的 std::string 物件存在期間有效。一旦 std::string 物件被銷燬或修改,這個指標就可能指向無效的記憶體。
程式碼QString.toStdString().c_str() 的行為可能產生意外的結果,尤其是在處理臨時字串時。
在使用 QString::toStdString().c_str() 時,需要注意幾個關鍵點:
- 臨時字串的問題:toStdString() 返回的是一個臨時 std::string 物件。這個臨時物件在呼叫 c_str() 時存在,但一旦 toStdString() 返回的 std::string 物件離開其作用域或被銷燬,該臨時物件也隨之消失。因此,如果 c_str() 是在這個臨時物件被銷燬後呼叫的,返回的指標將指向一個無效的記憶體位置,這可能導致未定義的行為或程式崩潰。
- 避免的方法:為了避免上述問題,最佳實踐是先將 QString 轉換為 std::string,然後再呼叫該 std::string 的 c_str() 方法。這樣做可以確保在呼叫 c_str() 時,std::string 物件仍然存在且有效。
- 特定環境的問題:在某些特定環境下,如 Windows 7 上使用 QString::toStdString() 可能會出現問題,尤其是在進行裝置連線等操作時。這可能是因為作業系統或環境配置的不同導致的。
綜上所述,雖然 QString::toStdString().c_str() 在某些情況下可能工作正常,但存在潛在的風險。開發者應當謹慎使用,並考慮上述提到的最佳實踐,以確保程式碼的健壯性和穩定性。
3. 修復問題原始碼
3.1 修復方式一
class KZNCalculationException : public std::exception
{
public:
#ifdef KZN_LINUX
KZNCalculationException(GString AMsg, GString AHit)
: std::exception()
{
QByteArray ba = AMsg.toLocal8Bit();
m_sMsg = new char[ba.size() + 1];
strcpy(m_sMsg, ba.data());
m_sHit = AHit;
}
#else
KZNCalculationException(GString AMsg, GString AHit)
: std::exception(AMsg.toStdString().c_str())
{
m_sHit = AHit;
}
#endif
GString getHit()
{
return m_sHit;
}
#ifdef KZN_LINUX
virtual const char* what() const throw (){
return m_sMsg;
}
#endif
private:
GString m_sHit;
#ifdef KZN_LINUX
char * m_sMsg;
#endif
};
3.2 修復方式二
class KZNCalculationException : public std::exception
{
public:
#ifdef KZN_LINUX
KZNCalculationException(GString AMsg, GString AHit)
: std::exception()
{
m_sMsg = AMsg.toStdString();
m_sHit = AHit;
}
#else
KZNCalculationException(GString AMsg, GString AHit)
: std::exception(AMsg.toStdString().c_str())
{
m_sHit = AHit;
}
#endif
GString getHit()
{
return m_sHit;
}
#ifdef KZN_LINUX
virtual const char* what() const throw (){
return m_sMsg.c_str();
}
#endif
private:
GString m_sHit;
#ifdef KZN_LINUX
std::string m_sMsg;
#endif
};