我有一个自定义的FastStack类,实现为固定大小的数组和该数组的索引。
在我的复制构造函数中,我分配数组,然后将每个对象从副本的数组分配到新数组中。在堆栈中的对象中有一些引用计数,因此使用赋值而不是简单的副本。
问题在于,在分配数组时,它有时会覆盖其他堆栈数组的一部分。可以预料,当数据被解除引用时,这会导致最终的分段错误。
class FastStack {
private:
int m_size, m_ptr;
ObjectRef* m_stack;
public:
FastStack(int size) : m_size(size), m_ptr(-1) {
m_stack = new ObjectRef[m_size];
}
FastStack(const FastStack& copy) : m_size(copy.m_size), m_ptr(copy.m_ptr) {
long a = (long)copy.m_stack[0];
m_stack = new ObjectRef[m_size];
if ((long)copy.m_stack[0] != a)
fprintf(stderr, "\nWe have a serious problem!\n\n");
for (int i = 0; i <= m_ptr; i++)
m_stack[i] = copy.m_stack[i];
}
~FastStack() {
delete[] m_stack;
}
};
class ObjectRef {
private:
DataObj* m_obj;
public:
ObjectRef() : m_obj(0) { }
ObjectRef(DataObj* obj) : m_obj(obj) {
if (m_obj) m_obj->addRef();
}
ObjectRef(const ObjectRef& obj) : m_obj(obj.m_obj) {
if (m_obj) m_obj->addRef();
}
~ObjectRef() {
if (m_obj) m_obj->delRef();
}
ObjectRef& operator=(DataObj* obj) {
if (obj) obj->addRef();
if (m_obj) m_obj->delRef();
m_obj = obj;
return *this;
}
ObjectRef& operator=(const ObjectRef& obj) {
if (obj.m_obj) obj.m_obj->addRef();
if (m_obj) m_obj->delRef();
m_obj = obj.m_obj;
return *this;
}
};
我看到“我们有一个严重的问题!”在segfault之前不久,并使用gdb单步执行它我可以看到new创建的ObjectRef之一与另一个堆栈的数组具有相同的地址。
我的第一直觉是说新的永远不应该分配已经在使用的内存,但这似乎就是这种情况,我完全不知道可以做些什么。
补充:当我看到这种情况发生时,m_size = 2且m_ptr = 0.
此程序的完整代码位于https://github.com/dvpdiner2/pycdc的github上,但非常复杂且难以理解。
答案 0 :(得分:3)
有时new
已经知道已经分配给其他对象的内存,当发生这种情况时,好的C ++代码必须重复分配,希望不与其他人共享内存......
new
没问题,你的程序有问题。在黑板上写下这一百次。
new
永远不会给你给别人的记忆但是如果你以前做过任何不好的事情(即访问超出限制,一旦指针被取消引用它指向已经解除分配,使用无效的迭代器和数以万计的其他可能的“UB”违规行为)然后new
获得特殊许可,可以做任何喜欢的事情,包括让守护进程出现在你的nosrils之外。
您的代码显示高度可疑但我没有看到任何肯定错误(隐藏了太多代码,例如究竟是什么是ObjRef和如何宣布)。从我看到的情况来看,我几乎肯定你在进行新的分配之前做错了一些因为很多违反了良好的C ++实践(例如显示的类有一个构造函数和一个复制构造函数,但是没有赋值运算符也没有析构函数)
然而,显示的代码的最大问题是看起来像是一个半支持和错误的尝试模仿普通std :: vector会做什么的子集。准确地说出问题是什么(但是)需要更多的上下文...这是错误的代码,但可能是合法的,这取决于其他代码的编写方式。很明显,即使是这一小段代码也已经被改变和减少,因为没有方法可以访问任何东西,没有朋友和数据成员是私有的(因此基本上不可能对这些对象做任何事情)。答案 1 :(得分:1)
在循环中,您从0迭代到m_ptr
:
for (int i = 0; i <= m_ptr; i++)
m_stack[i] = copy.m_stack[i];
但是,您的数组m_stack
包含m_size
个元素(假设您已初始化它们)。
编辑:重叠m_stack
和copy.m_stack
的唯一方式(我可以看到)是使用了展示位置new
。但是,根据发布的消息来源,你没有这样做。
答案 2 :(得分:1)
您应该使用std::vector
来管理内存,并保持您明确管理的索引(m_ptr)。这将解决所有这些问题。
然而,我真的看不出你的代码到底出了什么问题。认为自己是优化者,并快速通过,只关注a
和copy.m_stack[0]
。
long a = (long)copy.m_stack[0];
m_stack = new ObjectRef[m_size];
if ((long)copy.m_stack[0] != a)
long a = (long)copy.m_stack[0];
if ((long)copy.m_stack[0] != a)
if (copy.m_stack[0] != copy.m_stack[0])
如果该条件为真,那么您必须已损坏堆,并且可能但堆栈的可能性较小。堆损坏可能会将明显完全无关的代码咬入错误的代码中。要么就是这样,要么就是搞砸线程而不是告诉我们。 (并且弄错了)。
当然,你缺少其他几个重要的函数,比如operator =和destructor,但是嘿 - 如果你使用了一个向量,你就不必自己实现。
答案 3 :(得分:0)
在我看来,最可能的罪魁祸首实际上是复制构造函数的输入。
但是,我会说这段代码没有做足够的验证,而且它的构建方式会使得很难检测到错误或误用,直到它在某些时候崩溃。正如您现在所看到的,以这种方式编写的类将产生难以发现的错误。
你应该做的简单事情: 1.处理新抛出异常 2.在索引数组之前检查边界,即使你只是使用m_stack [0] 3.检查复制构造函数的输入是否有效(也称为初始化)
哦,跟踪这样的内存错误的最简单方法就是对它进行修改。它很快就能很好地消除内存问题,甚至认为这不是它的既定目标。
如果你包含一个工作的代码块,以最简单的可重复形式隔离问题,那么调试也会非常简单。
答案 4 :(得分:0)
您可以替换由此发布的整个代码:
class FastStack {
public:
protected:
std::vector<boost::shared_ptr<DataObj> > m_Stack;
};
(如果你需要添加一个构造函数来确保向量已经预先分配了它的内存)。
当你在做不必要的工作时,通过实施其他人已经做得更好的课程(“其他人”在这种情况下是聪明人,比我们大多数人更聪明),你很可能会增加更多问题比你解决。
std :: vector将是一个比你分配的数组和boost::shared_ptr(或tr1 :: shared_ptr或std :: shared_ptr,如果你使用tr1 / c ++ 0x兼容的编译器)更好的集合)正在做你的ObjectRef类正在做的事情(但更好)。在您的代码中,似乎DataObj包含自己的引用计数,如果您使用shared_ptrs,也可以废弃。
使用可用的工具 - 编写的代码越少,编写错误的可能性就越小......如果用上面的代码替换代码并仍然遇到问题,那么错误就在其他地方。< / p>
答案 5 :(得分:0)
可能是您的代码适用于多个线程吗?
由于new永远不会返回已经分配的内存,因此获取检查消息的唯一方法是另一个线程正在对副本实例执行某些操作。
这种怀疑也因为它只是不时发生而引起的。这通常用于多线程错误(因为它们需要一个特殊的时间发生,只会偶尔发生)。