Вопросы по поводу ошибки сегментации в C ++, скорее всего, вызванной пользовательским конструктором копирования - PullRequest
0 голосов
/ 25 августа 2011

Я получаю ошибку сегментации, которая, по моему мнению, вызвана конструктором копирования. Тем не менее, я не могу найти подобный пример в Интернете. Я читал о мелкой и глубокой копии, но я не уверен, к какой категории относится эта копия. Кто-нибудь знает?

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

РЕДАКТИРОВАТЬ: добавлены оператор = и конструктор

решено: лай неправильного дерева, в итоге это была функция, вызывающая удаление дважды для одного и того же объекта

Ответы [ 7 ]

2 голосов
/ 25 августа 2011

Обычно плохая идея использовать такой оператор присваивания в конструкторе копирования.Это создаст по умолчанию все элементы и затем назначит их.Гораздо лучше либо просто положиться на неявно сгенерированный конструктор копирования, либо использовать список инициализаторов элементов для копирования тех членов, которые нуждаются в копировании, и применить соответствующую инициализацию к другим.

Без подробностей о классеучастники, трудно судить, что является причиной вашего segfault.

1 голос
/ 25 августа 2011

Согласно вашему коду вы не создаете исходный объект ... вы просто создаете указатель так: const MyObject * mpoOriginal;

Итак, копия использует неверные данные в созданном новом объекте ...

0 голосов
/ 25 августа 2011

Вау ....

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;
}
0 голосов
/ 25 августа 2011
MyObject::MyObject{
    lots of things including const and structs, but no pointers
}

Разница между мелкой копией и глубокой копией имеет смысл только при наличии указателя на динамическую память.Если любой из этих структур-членов не делает глубокую копию своего указателя, то вам придется обойти это (как это зависит от структуры).Однако, если все члены либо не содержат указателей, либо правильно делают глубокие копии своих указателей, то конструктор / назначение копирования не является источником ваших проблем.

0 голосов
/ 25 августа 2011

У меня нет прямого ответа относительно того, что именно вызывает segfault, но общепринятая мудрость здесь состоит в том, чтобы следовать правилу из трех , то есть, когда вы обнаружите, что нуждаетесь в любой конструктор копирования, оператор присваивания или деструктор, вам лучше реализовать все три из них ( c ++ 0x добавляет семантика перемещения что делает его "правилом четырех"?).

Тогда, как правило, все наоборот - оператор назначения копирования реализован в терминах конструктора копирования - идиома копирования и обмена .

0 голосов
/ 25 августа 2011

Что вы делаете, это заставляете ваш конструктор копирования использовать оператор присваивания (который вы, похоже, не определили). Честно говоря, я удивлен, что он компилируется, но, поскольку вы не показали весь свой код, возможно, он это сделает.

Напишите, что вы копируете конструктор обычным способом, а затем посмотрите, не возникла ли у вас такая же проблема. Если это правда, что вы говорите о «многих вещах ... но я не вижу никаких указателей», тогда вам вообще не следует писать конструктор копирования. Попробуйте просто удалить его.

0 голосов
/ 25 августа 2011

Это также зависит от того, что делает operator=. Вот где происходит магия; конструктор копирования просто вызывает его.

Если вы сами не определили operator=, то компилятор синтезировал его для вас, и он выполняет мелкую копию.

...