2016-02-27 2 views
2

Я пытаюсь понять концепцию конструктора копирования. Я использовал этот пример:Функция конструктора копирования

#include <iostream> 

using namespace std; 

class Line 
{ 
public: 
    int GetLength(); 
    Line(int len); 
    Line(const Line &obj); 
    ~Line(); 

private: 
    int *ptr; 
}; 

Line::Line(int len) 
{ 
    ptr = new int; 
    *ptr = len; 
}; 

Line::Line(const Line &obj) 
{ 
    cout << "Copying... " << endl; 
    ptr = new int; 
    *ptr = *obj.ptr; 
}; 

Line::~Line() 
{ 
    delete ptr; 
}; 

int Line::GetLength() 
{ 
    return *ptr; 
} 

int main() 
{ 
    Line line1 = Line(4); 
    cout << line1.GetLength() << endl; 

    Line line2 = line1; 
    line1.~Line(); 

    cout << line2.GetLength() << endl; 

    return 0; 
} 

Вопрос в том, почему я получаю ошибку времени выполнения здесь? Если я определил конструктор копирования, который выделяет память для нового ptr и назначил строку 1 на строку 2, не означает ли это, что эти два являются отдельными объектами? Разрушив строку1, я, очевидно, испортил строку2, или я неправильно использую вызов деструктора?

+2

Это хорошая привычка реализовать оператор присваивания. Но в вашем случае проблема заключается в явном вызове деструктора, ваш указатель будет удален дважды, что приведет к ошибке выполнения. –

+1

Почему вы делаете это 'line1. ~ Line();'? Кто вам сказал? – juanchopanza

+0

Это просто для практики, я попробовал это, чтобы увидеть, что происходит с объектом line2, когда я разрушаю строку1. Я пришел из мира C# :) – omegasbk

ответ

7

Вы назвали деструктор в этом заявлении

line1.~Line(); 

, который удаляется из памяти, выделенной для ptr

Line::~Line() 
{ 
    delete ptr; 
}; 

Однако объект line1 жив, потому что он имеет автоматический срок хранения. Поэтому после выхода из главного деструктор для объекта будет называться еще одним, и в результате он попытается удалить память, на которую указывает ptr, которая была явно удалена.

+4

Технически это неопределенное поведение, как только деструктор вызывается во второй раз ([class.dtor]/15) –

0
line1.~Line(); 

вызов деструктора вручную полезен только при использовании размещения нового.

Я не понимаю, что дало вам идею вызвать деструктор вручную в этой программе. Вы действительно не хотите знать, такой механизм управления памятью низкого уровня, когда вы еще новичок в языке, но для полноты картины, она будет работать так:

int main() 
{ 
    // provide static memory with enough space for one Line object: 
    char buffer[sizeof(Line)]; 

    // create a Line object and place it into buffer: 
    Line* line1 = new (buffer) Line(4); 

    cout << line1->GetLength() << endl; 

    Line line2 = *line1; 

    // manually call the destructor: 
    line1->~Line(); 

    cout << line2.GetLength() << endl; 

    // - no delete necessary because buffer disappears automatically 
    // - no automatic destructor call 

    return 0; 
} 

Ваш код, однако , приводит к попытке дважды вызвать деструктор line1. Сначала вручную, затем автоматически, когда область объекта заканчивается, т. Е. В конце main. Это undefined поведение. См. Does explicitly calling destructor result in Undefined Behavior here?

Вопрос в том, почему я получаю ошибку времени выполнения здесь?

Поскольку неопределенное поведение означает, что ваша программа может делать или ничего не делать. Ошибка выполнения не гарантируется, и вы не гарантируете какого-либо детерминированного поведения вообще.

+0

Я думал, что ручное инициирование деструктора вызывает освобождение памяти, хранящейся у объекта. Я хотел быть уверенным, что у меня есть новый объект один раз, когда я назначаю line1 в строку2. – omegasbk

+1

@omegasbk: Деструкторы никогда не освобождают память, занимаемую их собственным объектом. Это не их цель. Само слово «деструктор» может быть запутанным; его технически следует называть «do_stuff_before_object_is_destructed_by_someone_else». –

+0

A-ha, имеет смысл! Благодаря! – omegasbk

Смежные вопросы