一个有趣的Bug修正记
2010-07-22 21:15
357 查看
最近无聊写了一个Managed类用来托管指针(C++中)。代码如下:(不存在内存泄露)
这个类有个很严重的问题,就是使用链表的时候。比如有如下的链表定义:
这是一个很一般的链表节点结构。下面,我们使用这个结构:
这个看起来是没什么问题,但是运行的时候有Access Violation。前后加入调试语句之后,发现问题出现在:
p = p->next; (在输出那段里面)
这个有什么问题呢?好像是对象已经被回收了。神奇啊!看看Managed的代码。
在“=运算符”中,有个很大的问题:
问题出现在“this->~Managed();”这里。经过一番思考和画图后,发现p = p->next;的执行方式如下:
p.operator = (p.operator->()->next);
调用operator->没问题。
调用operator=:
调用析构函数
析构对象(p的当前对象)
之前,head=NULL,已经回收了head(head这里是一个无效数据),所以p的引用计数为0,回收p托管的对象。
回收p托管的对象之后,p托管的对象的next的引用计数也变成0,顺便也回收了,就这样,把后面的节点都回收了……
析构完毕
p托管了不可用内存(有的人称为“野指针”“悬空指针”“悬垂指针”)。
结束调用operator=。
然后下一轮循环的时候,就出现了Access Violation。
于是,代码改为使用备份:
至此,问题解决。希望本文能给广大Developer一些Debug的帮助!
by Logic 版权所有,未经许可,不得转载
namespace Logic { template <class T> class Managed { T *datum; int *refCnt; public: //Constructors Managed() : datum(0), refCnt(0) { } Managed(const Managed<T> &src) : datum(src.datum), refCnt(src.refCnt) { if (refCnt) ++*refCnt; } Managed(T *src) : datum(0), refCnt(0) { if (src) { datum = src; refCnt = new int(1); } } //Destructor ~Managed() { if (refCnt && !--*refCnt) { delete datum; delete refCnt; } } //Cast Operators const T &operator * () const { return *datum; } T &operator * () { return *datum; } const T *operator -> () const { return datum; } T *operator -> () { return datum; } operator bool() { return datum; } //Assignment Operators const Managed<T> &operator = (const Managed<T> &src) { this->~Managed(); datum = src.datum; refCnt = src.refCnt; if (refCnt) ++*refCnt; return *this; } const Managed<T> &operator = (T *src) { this->~Managed(); if (src) { datum = src; refCnt = new int(1); } else { datum = 0; refCnt = 0; } return *this; } //Comparement Operators bool operator == (const Managed<T> &rval) { return datum == rval.datum; } bool operator == (T *rval) { return datum == rval; } bool operator != (const Managed<T> &rval) { return !operator == (rval); } bool operator != (T *rval) { return !operator == (rval); } }; }
这个类有个很严重的问题,就是使用链表的时候。比如有如下的链表定义:
typedef struct node_ { int datum; Managed<struct node_> next; node_() { datum = 0; next = 0; } } node;
这是一个很一般的链表节点结构。下面,我们使用这个结构:
int main() { int n; cin >> n; Managed<node> head, p; head = new node(); p = head; for (int i = 0; i < n; ++i) { p = p->next = new node(); cin >> p->datum; } p = head->next; head = NULL; while (p) { cout << p->datum << ' '; p = p->next; } system("pause"); return 0; }
这个看起来是没什么问题,但是运行的时候有Access Violation。前后加入调试语句之后,发现问题出现在:
p = p->next; (在输出那段里面)
这个有什么问题呢?好像是对象已经被回收了。神奇啊!看看Managed的代码。
在“=运算符”中,有个很大的问题:
const Managed<T> &operator = (const Managed<T> &src) { this->~Managed(); datum = src.datum; refCnt = src.refCnt; if (refCnt) ++*refCnt; return *this; } const Managed<T> &operator = (T *src) { this->~Managed(); if (src) { datum = src; refCnt = new int(1); } else { datum = 0; refCnt = 0; } return *this; }
问题出现在“this->~Managed();”这里。经过一番思考和画图后,发现p = p->next;的执行方式如下:
p.operator = (p.operator->()->next);
调用operator->没问题。
调用operator=:
调用析构函数
析构对象(p的当前对象)
之前,head=NULL,已经回收了head(head这里是一个无效数据),所以p的引用计数为0,回收p托管的对象。
回收p托管的对象之后,p托管的对象的next的引用计数也变成0,顺便也回收了,就这样,把后面的节点都回收了……
析构完毕
p托管了不可用内存(有的人称为“野指针”“悬空指针”“悬垂指针”)。
结束调用operator=。
然后下一轮循环的时候,就出现了Access Violation。
于是,代码改为使用备份:
const Managed<T> &operator = (const Managed<T> &src) { T *backupDatum = datum; int *backupRefCnt = refCnt; // 备份对象和引用计数 datum = src.datum; refCnt = src.refCnt; if (refCnt) ++*refCnt; if (backupRefCnt && !--*backupRefCnt) { delete backupDatum; delete backupRefCnt; } return *this; } const Managed<T> &operator = (T *src) { T *backupDatum = datum; int *backupRefCnt = refCnt; if (src) { datum = src; refCnt = new int(1); } else { datum = 0; refCnt = 0; } if (backupRefCnt && !--*backupRefCnt) { delete backupDatum; delete backupRefCnt; } return *this; }
至此,问题解决。希望本文能给广大Developer一些Debug的帮助!
by Logic 版权所有,未经许可,不得转载
相关文章推荐
- 一个有趣的和时钟相关的bug
- C++标准库的一个有趣的小bug
- ALinq for Oracle(V2.2) 日期 映射的一个 BUG(已修正)
- C++标准库的一个有趣的小bug
- 修正o-blog 2.5的一个bug
- 一个有趣的bug
- Linus修正一个内核的mmap data corrupt bug
- magento -- 修正又一个翻译上的bug,同时了解了下Magento解析xml的方式
- 播客:IE7的一个有趣的BUG
- Geogebra里面一个有趣的LaTeX代码bug
- 今天修正了一个SMD数据库的BUG
- 一个有趣的小 Bug 避免了一场大灾难
- 一个有趣的现象(苹果的bug Or 坑?),关于区分真机和模拟器的预编译宏
- U3Terrain的一个BUG及修正
- BugFree 的一个 bug 修正
- Jenkins的构建编号和一个有趣的bug
- C#压缩解压缩 网上提供的一个解决方案的BUG修正
- Google AJAXSLT 的一个bug修正
- [置顶] 我修正的 modalbox 的一个bug
- C++标准库的一个有趣的小bug