c++偶現問題備錄

kaizenly發表於2024-08-29

目錄
  • 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() 時,‌需要注意幾個關鍵點:‌

  1. 臨時字串的問題:‌toStdString() 返回的是一個臨時 std::string 物件。‌這個臨時物件在呼叫 c_str() 時存在,‌但一旦 toStdString() 返回的 std::string 物件離開其作用域或被銷燬,‌該臨時物件也隨之消失。‌因此,‌如果 c_str() 是在這個臨時物件被銷燬後呼叫的,‌返回的指標將指向一個無效的記憶體位置,‌這可能導致未定義的行為或程式崩潰。‌
  2. 避免的方法:‌為了避免上述問題,‌最佳實踐是先將 QString 轉換為 std::string,‌然後再呼叫該 std::string 的 c_str() 方法。‌這樣做可以確保在呼叫 c_str() 時,‌std::string 物件仍然存在且有效。‌
  3. 特定環境的問題:‌在某些特定環境下,‌如 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
};

相關文章