有关C ++中的分段错误的问题很可能是由自定义复制构造函数引起的

时间:2011-08-25 16:26:58

标签: c++ segmentation-fault copy-constructor deep-copy shallow-copy

我遇到了一个分段错误,我认为这是由复制构造函数引起的。但是,我无法在网上任何地方找到这样的例子。我读过关于浅拷贝和深拷贝但我不确定这个拷贝属于哪个类别。有人知道吗?

MyObject::MyObject{
    lots of things including const and structs, but no pointers
}
MyObject::MyObject( const MyObject& oCopy){
    *this = oCopy;//is this deep or shallow?
}
const MyObject& MyObject::operator=(const MyObject& oRhs){
    if( this != oRhs ){
        members = oRhs.members;
        .....//there is a lot of members
    }
    return *this;
}
MyObject::~MyObject(){
    //there is nothing here
}

代码:

const MyObject * mpoOriginal;//this gets initialized in the constructor

int Main(){
    mpoOriginal = new MyObject();
    return DoSomething();
}

bool DoSomething(){
    MyObject *poCopied = new MyObject(*mpoOriginal);//the copy
    //lots of stuff going on
    delete poCopied;//this causes the crash - can't step into using GDB
    return true;
}

编辑:添加了operator =和构造函数

解决:咆哮错误的树,它最终成为在同一个对象上调用删除两次的函数

7 个答案:

答案 0 :(得分:2)

在复制构造函数中使用这样的赋值运算符通常是个坏主意。这将默认构造所有成员,然后分配它们。要么只依赖隐式生成的复制构造函数,要么使用成员初始化列表来复制那些需要复制的成员,并将适当的初始化应用于其他成员。

如果没有班级成员的详细信息,很难判断导致您的段错误的原因。

答案 1 :(得分:1)

根据你的代码,你没有创建原始对象......你只是创建一个这样的指针: const MyObject * mpoOriginal;

因此副本将错误数据用于创建的新对象......

答案 2 :(得分:0)

您正在做的是让复制构造函数使用赋值运算符(您似乎没有定义)。坦率地说,我很惊讶它编译,但因为你没有显示所有代码,也许它确实如此。

以正常方式编写复制构造函数,然后查看是否仍然遇到同样的问题。如果你对“很多东西......但我没有看到任何指针”的说法是真的那么你根本不应该写一个复制构造函数。尝试删除它。

答案 3 :(得分:0)

我没有直接回答究竟是什么导致了段错误,但这里的传统智慧是遵循rule of three,即当你发现自己需要任何时复制构造函数,赋值运算符或析构函数,您最好实现它们的所有三个c++0x添加移动语义,这使得它成为"规则4"。)

然后,它通常是相反的 - 复制赋值运算符是根据复制构造函数实现的 - copy and swap idiom

答案 4 :(得分:0)

MyObject::MyObject{
    lots of things including const and structs, but no pointers
}

浅拷贝和深拷贝之间的区别仅在存在指向动态内存的指针时才有意义。如果这些成员结构的任何没有对它的指针进行深层复制,那么你将不得不解决它(如何取决于结构)。但是,如果所有成员要么不包含指针,要么正确地执行指针的深层复制,那么复制构造函数/赋值不是问题的根源。

答案 5 :(得分:0)

...哇

MyObject::MyObject( const MyObject& oCopy)
{
    *this = oCopy;//is this deep or shallow?
}

它既不是。这是对分配操作员的调用 由于你还没有完成对象的构造,这可能是不明智的(虽然完全有效)。根据复制构造函数定义赋值运算符更为传统(参见copy和swap idium)。

const MyObject& MyObject::operator=(const MyObject& oRhs)
{
    if( this != oRhs ){
        members = oRhs.members;
        .....//there is a lot of members
    }
    return *this;
}

基本上很好,但通常分配的结果不是续 但是,如果你这样做,你需要将处理分开一点,以使其异常安全。看起来应该更像这样:

const MyObject& MyObject::operator=(const MyObject& oRhs)
{
    if( this == oRhs )
    {
        return *this;
    }
    // Stage 1:
    // Copy all members of oRhs that can throw during copy into temporaries.
    // That way if they do throw you have not destroyed this obbject.

    // Stage 2:
    // Copy anything that can **not** throw from oRhs into this object
    // Use swap on the temporaries to copy them into the object in an exception sage mannor.

    // Stage 3:
    // Free any resources.
    return *this;
}

当然,使用copy和swap idum有一种更简单的方法:

MyObject& MyObject::operator=(MyObject oRhs)  // use pass by value to get copy
{
    this.swap(oRhs);
    return *this;
}

void MyObject::swap(MyObject& oRhs)  throws()
{
    // Call swap on each member.
    return *this;
}

如果析构函数中没有任何操作,请不要声明它(除非它需要是虚拟的)。

MyObject::~MyObject(){
    //there is nothing here
}

这里你声明一个指针(不是一个对象)所以不调用构造函数(因为指针不具有构造函数)。

const MyObject * mpoOriginal;//this gets initialized in the constructor

在这里,您要调用new来创建对象 你确定你要这么做吗?必须销毁动态分配的对象;表面上通过删除,但通常在C ++中,您将指针包装在智能指针内,以确保所有者正确并自动销毁该对象。

int main()
{ //^^^^   Note main() has a lower case m

    mpoOriginal = new MyObject();
    return DoSomething();
}

但是因为你可能不想要动态对象。你想要的是自动对象在超出范围时被销毁。此外,您可能不应该使用全局变量(将其作为参数传递,否则您的代码使用与全局状态关联的副作用)。

int main()
{
     const MyObject  mpoOriginal;
     return DoSomething(mpoOriginal);
}

您不需要调用new来制作副本只需创建一个对象(传递您要复制的对象)。

bool DoSomething(MyObject const& data)
{
    MyObject   poCopied (data);     //the copy

    //lots of stuff going on
    // No need to delete.
    // delete poCopied;//this causes the crash - can't step into using GDB
    // When it goes out of scope it is auto destroyed (as it is automatic).

    return true;
}

答案 6 :(得分:-1)

取决于你operator=的作用,它也是。这就是神奇发生的地方;复制构造函数只是调用它。

如果您没有自己定义operator=,那么编译器会为您合成一个,并且它正在执行浅层复制。