在一个巨大的数据文件作为输入时, 出现了一个assert断言错误. 花了半天时间跟踪代码, 费尽心思找到了错误的来源, 可是一时间也没有解决的办法, 只能打个临时补丁.
半天时间找到原因却仍未解决的Bug
在一个巨大的数据文件作为输入时, 出现了一个assert断言错误.
花了半天时间跟踪代码, 费尽心思找到了错误的来源,
可是一时间也没有解决的办法, 只能打个临时补丁.
类里面有个成员变量
std::ofstream m_ofs;
在某个方法里面的断言警告:
assert(!m_ofs);
流操作之后都有一个状态判断:
if (!m_ofs)
{
log...
m_ofs.close();
return;
}
为什么会有错误的流状态?
但是open()操作后判断失败没有关闭流.
m_ofs.open(sPath);
if (!m_of)
{
log...
return;
}
表面上看是ofstream::open()打开文件出错了.
open()出错时只写了一条日志, 没做处理.
这个ofstream对象m_ofs不是临时对象, 而是类内嵌对象.
下一次使用时, 发现m_ofs处于已打开状态, 而流状态是错误的,
以为open()失败后文件会处于关闭状态, 看来是错误的.
open()失败后加了一个close()试了试,还是有流错误。
open()为什么会出错? strerror(errno)显示"No error".
以为是errno没有正确指示错误, 再跟踪进入open()代码, 发现open()其实是成功了.
m_ofs是在open()之前就已经fail了(查看内部状态State=2, 即failbit位).
一定是上次close()后failbit并没有清除.
close()成功情况下, 会执行clear(), 但有可能close()失败.
添加clear()在所有close()之前.
可是failbit象是凭空出现, clear()并没有起作用.
回忆历史代码, 原来只有临时变量的流, 用完就自动关闭了.
而为了对付频繁的同一文件打开, 流对象改为了内部成员变量.
而流的宿主类间接地是另一个类的成员变量, 该类是某stl容器类的元素.
问题就在这里了, stl容器类会使用其元素类的拷贝构造函数来复制元素.
一定是ofstream通过这样的复制后产生了错误的状态.
而宿主类及其上层也没有提供拷贝构造函数, 只有默认的浅拷贝.
如果没有这个流, 这些类都是一些简单数据类, 完全可以使用默认的字节拷贝.
浅拷贝不行, 深拷贝不能, 结果是找到原因, 却不知如何修改.
只能暂时恢复为临时流变量.
不过最终还应该支持大量的文件打开关闭操作, 不知怎么办.
心得:
* 没有必要就不要保存一个流变量,使用临时变量可保证文件打开与关闭是成对的。
* stl容器最好只存放简单数据,可避免自定义拷贝构造函数。
如果元素类较复杂,应考虑使用指针,因为有可能今后的修改后使元素类更复杂。
* ofstream::close()可能不会清除错误状态,并可能置failbit,如未打开就关闭。
上面的clear()应加在close()之后。
问题:
* 如何添加单元测试,使得一个类保证为简单数据类,或保证stl容器可安全使用?
* 不能实现深拷贝时,有没有办法解决浅拷贝问题?