c++偶现问题备录(QString.toStdString().c_str())

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 修复方式二(利用std::string缓存)

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
};
posted @ 2024-08-29 20:57  kaizenly  阅读(41)  评论(0编辑  收藏  举报
打赏