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

Questions about a Segmentation Fault in C++ most likely caused by a custom copy constructor

本文关键字:自定义 复制 构造函数 很可能 C++ 分段 错误 问题 关于      更新时间:2023-10-16

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

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;
}

EDIT:添加了运算符=和构造函数

解决方法:找出错误的树,它最终成为一个在同一对象上两次调用delete的函数

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

如果没有类成员的详细信息,很难判断是什么导致了您的segfault。

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

所以副本正在使用坏数据到创建的新对象中。。。

哇。。。。

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

两者都不是。这是对分配操作员的调用
由于您还没有完成对象的构造,这可能是不明智的(尽管完全有效(。不过,更传统的做法是根据复制构造函数来定义赋值运算符(请参阅复制和交换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;
}

当然,使用复制和交换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;
}

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

以正常的方式编写复制构造函数,然后看看是否还会遇到同样的问题。如果你说的很多事情都是真的。。。但是我看不到任何指针,那么你根本不应该编写复制构造函数。尝试删除它。

对于segfault的确切原因,我没有直接的答案,但这里的传统智慧是遵循三规则,即当您发现自己需要任何的复制构造函数、赋值运算符或析构函数时,您最好实现所有三个(c++0x添加移动语义,这使其成为"四规则"?(。

然后,通常是相反的方式-复制赋值运算符是根据复制构造函数实现的-复制和交换习惯用法。

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

只有当有指向动态内存的指针时,浅拷贝和深拷贝之间的区别才有意义。如果这些成员结构中的任何都没有对其指针进行深度复制,那么您将不得不解决这个问题(具体如何取决于结构(。但是,如果所有成员都不包含指针,或者正确地对其指针进行了深度复制,那么复制构造函数/赋值就不是问题的根源。

两者皆有,具体取决于operator=的功能。这就是魔法发生的地方;复制构造函数只是在调用它。

如果你自己没有定义operator=,那么编译器会为你合成一个,它正在执行一个浅拷贝。