1.发现问题
我在最开始使用Qt的时候,将双精度类型的数字转化为一定精度的字符串,使用了如下的函数:
QString &QString::setNum(double n, char format = 'g', int precision = 6)
如果想将一个数字赋值给QString类型的话,没有如下的函数:
QString::QString(double d)
因此如果想讲一个数字转换成字符串的话,就需要使用QString::setNum()
函数。由于在我的一段不是很长的程序里面遇到好几个这个设置的内容,我就想着使用一个变量名,然后在不同使用的时候直接将对象进行这样setNum()
一下就OK了。具体的使用如下所示:
QString str;
// 其他非必要代码
tempItemString = QString(
",{\"autoContinue\":true,\"command\":16,"
"\"coordinate\":[%1,%2,%3],\"frame\":3,\"id\":%4,"
"\"param1\":0,\"param2\":0,\"param3\":0,\"param4\":0,"
"\"type\":\"missionItem\"}")
.arg(str.setNum(point.getLon(),'g',10))
.arg(str.setNum(point.getLan(),'g',10))
.arg(str.setNum(point.getH(),'g',10))
.arg(temp_doJumpId++);
在最开始代码里面运行的时候,这个是完全没有问题的,并且能够完成我的思路和任务。
但是在另一个地方将这个程序片段取出来之后,再次运行类是的任务,却出错了。在下面的程序中,
.arg(str.setNum(point.getLon(),'g',10)) // 得到的是:point.getLon()
.arg(str.setNum(point.getLan(),'g',10)) // 得到的是:point.getLon()
.arg(str.setNum(point.getH(),'g',10)) // 得到的还是:point.getLon()
通过这三个参数进去的都是同样的值,在第一次设置之后,后面的两次值居然没有变化,但是在最开始较大的程序里面使用的时候却是正确的值。
2.测试函数正确性
我使用了如下的程序进行测试。
2.1 第一个测试
下面是第一个测试的代码:
QString str;
for(int i = 0;i < 1999;i++)
{
qDebug()<< str.setNum(i) << str.setNum(++i);
}
如果按照Qt的官方解释的使用,每行将会是不同的数值,例如第一行应该是0和1,但是测试的结果如下:
"1" "1"
"3" "3"
"5" "5"
"7" "7"
"9" "9"
"11" "11"
"13" "13"
// 其他数据省略
在上面的结果中输出的是第一个set进去的值。
2.2 第二个测试
下面是第二个测试的代码:
PointInfo point;
foreach (point, pointInfoList)
{
qDebug() << point.getH() << point.getLon() << point.getLan() << point.getG();
qDebug() << str.setNum(point.getH(),'g',10) << str.setNum(point.getLon(),'g',10) << str.setNum(point.getLan(),'g',10) << str.setNum(point.getG(),'g',10);
}
输出的结果如下:
30 116.499 39.7131 345
"30" "30" "30" "30"
40 116.499 39.7131 345
"40" "40" "40" "40"
50 116.499 39.7131 345
"50" "50" "50" "50"
在这个时候发现给出的是第一个set进去的值。
2.3 另一个成功躲过的测试
如上面所示的2.2和最上面发现问题时候运行的程序,每次都能得到想要的结果。
2.4 测试的结果总结
在这三种不同的测试中,结果各不同。有时候是第一个set进去的值起作用,有时候是第二个,还有时候是正确的。
3 分析存在这个问题的原因
再看一下原本的函数:
QString &QString::setNum(double n, char format = 'g', int precision = 6)
它返回的其实是一个引用(QString &
),而不是一个QString
,这也就是每次的都指向的str
本身,对于上面的2.1的测试就很容易理解了,其实运算是很快的,将str
所指向的位置两次给调试的输出流(qDebug()
),两次给的时间很短,因此是在内存中一次复制到流之中的,复制的时候是直接访问的同一个内存,因此得到的是后面改变之后的值。
但是,同时可能存在一个问题,这个仅仅是我的猜测,因为刚把第一个set进去,后面的还没有,但是可以访问到这个地址,直接就将值取出来了。但是这个仅仅是我的猜测,具体的没有去了解。
而对于我在较大的程序里面执行成功的问题可能是因为其他的运行影响了这个代码的执行速度,导致了每次访问那个内存地址的时候里面的值已经改变成后面的值了。
如果是上面的这两种可能性的话,也就是在往一个字符串里面进行设置值的时候不能这样使用,否则将会出现各种可能的错误。
在程序的使用中,其实Qt里面的代码没有按照原本的顺序执行,形成了不同的内容的优化,导致出现了这样的问题,我觉得这算一个Qt的bug吧,如果是按照正确的顺序的话,不应该出现这样的问题的,也就是每一次需要等待执行完成括号里面的程序再执行外面的程序,这样可能会导致执行速度没有几个同样类型的内容一起执行快,但是按照逻辑更不容易出问题。
4 程序的方法改进
在上面,本着节约变量的创建去的,但是如果存在这样的问题的话,就每次需要的时候创建一个临时的变量吧,修改如下:
QString str;
// 其他非必要代码
tempItemString = QString(
",{\"autoContinue\":true,\"command\":16,"
"\"coordinate\":[%1,%2,%3],\"frame\":3,\"id\":%4,"
"\"param1\":0,\"param2\":0,\"param3\":0,\"param4\":0,"
"\"type\":\"missionItem\"}")
.arg(QString("").setNum(point.getLon(),'g',10))
.arg(QString("").setNum(point.getLan(),'g',10))
.arg(QString("").setNum(point.getH(),'g',10))
.arg(temp_doJumpId++);
这样的话,相当于每个都是一个新的对象了,就不会出现上面的问题了。