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() 时,需要注意几个关键点:
- 临时字符串的问题: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 修复方式二(利用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
};