2012-02-13 4 views
3

До сих пор я писал программы на Java. Поэтому, когда я начал C++, первое, что мне пришло в голову, это то, как уничтожить/удалить/завершить объекты, которые мне больше не нужны.Деструкторы в C++ (по сравнению с java)

С помощью Java я использовал их для null, поэтому сборщик мусора заботился об этом. Однако я не знаю, как это стоит с C++. Я нашел эту статью http://en.wikipedia.org/wiki/Comparison_of_Java_and_C%2B%2B, которая решила большинство моих вопросов. Но есть еще кое-что, чего я не понял.

1) В Java есть способ заставить сборщик мусора очищаться прямо на месте (что не всегда полезно, так как оно ожидает, что несколько мусора будут стекаться перед запуском). Есть ли способ сделать это с C++?

2) (C++) Кроме того, как это сделать, я могу сделать так, чтобы я поместил объект в состояние «помечено как удаленное», и программа решает, когда его очищать (например, Java)?

3) (C++) Должен ли я заставлять коллектор для варвара чистить прямо на месте (я уверен, что это не тот путь, но я прошу только убедиться)?

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

+9

C++ не предоставляет сборку мусора (вы можете реализовать его, но это настоящая боль в заднице и обычно не требуется). У этого есть [RAII] (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization), что делает деструкторы очень полезными. –

+5

Найдите книгу. C++ и Java в этой области отличаются друг от друга как автомобиль и поезд. –

+2

Вы читали статью, к которой вы связались? У C++ нет сборщика мусора. Ваши вопросы не имеют смысла. –

ответ

4

C++ очень отличается от Java в этой области, так вот краткий обзор:

распределение: память резервируется для объекта.
строительство: объект готов к использованию.
уничтожение: объект «завершает» все и разбирает себя.
освобождение: память возвращается в систему.

int main() { 
    int myint; //automatic int object is allocated and constructed 
    //stuff 
} // when main ends, automatic int object is destroyed and deallocated 

int main() { 
    int* mypointer; //automatic pointer object is allocated and constructed 
    mypointer = new int; //dynamic int object is allocated and constructed 
    //stuff 
    delete mypointer; //dynamic int object is destroyed and deallocated 
} // when main ends, automatic pointer object is destroyed and deallocated 
    // note: Pointers to _not_ delete the object they point to. 

class myclass { 
    //members 
public: 
    myclass() {} //this is the default constructor 
    myclass(const myclass& rhs) {} //this is the copy constructor 
    myclass& operator=(const myclass& rhs) {return *this} //this is the assignment operator 
    ~myclass() {} //this is the destructor 
}; 

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

Если вы выделите материал вручную (с ключевым словом new), он должен быть уничтожен и удален с использованием ключевого слова delete. Когда вы вызываете delete, он тут же будет уничтожать (и освобождать) и не будет продолжаться до тех пор, пока это не будет выполнено. Если вы забудете, он НИКОГДА НЕ ПОЛУЧИТ ДЕЙСТВУЮЩИЙ (все, операционные системы освободят его, когда закончится ваша программа).

Поскольку люди делают ошибки, «правильный», что нужно делать, когда вам нужно динамических объектов является:

int main() { 
    std::unique_ptr<myclass> myptr = new myclass(); //allocate and construct 
} //both the unique_ptr and the dynamic object are destroyed and deallocated 

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

Причина C++ заключается в том, что если у вас есть объект F, который представляет этот файл, он может иметь исключительную блокировку для этого файла. В C++, как только F уничтожен, вы можете сразу создать объект G, который использует тот же самый файл. В Java нет гарантии, что finalizer будет запустится, что означает, что файл может оставаться заблокированным до завершения вашей программы. (Маловероятно, но возможно)

1

Сбор мусора на C++ всегда немедленный. Нет отдельного сборщика мусора; при удалении объекта он немедленно удаляется по текущему потоку. Это выглядит следующим образом:

MyObject* foo = new MyObject(); 
... 
delete foo; 

Есть рамки сбора мусора доступны для C++, и вы также можете посмотреть в смарт-указатели, которые также являются формой сбора мусора.

Примечание. Комментарии Джеймса ниже - удаление деструктора и оператора для объекта всегда вызывается немедленно, но зависит от реализации, независимо от того, будет ли память сразу доступна.

+3

Просто ничто, но нет никакой гарантии, что когда вы свободной памяти в C++, он становится сразу доступным. Я знаю системы, в которых освобождение памяти, которое было выделено в другом потоке, отложено, и есть вероятность и других случаев. –

+0

И если вы не назовете 'delete', объект останется навсегда, даже если он станет недоступным. –

+0

@JamesKanze - исправьте меня, если я ошибаюсь, но сам деструктор всегда называется сразу же?Поскольку память становится доступной для будущих новых операторов, я предполагаю, что это больше зависит от реализации. –

6

1) Если объекты находятся в автоматическом хранилище, вы можете ограничить сферу их применения:

{ 
    X x; 
    //... 
} //x gets destructed here 

Если в оперативной памяти, то удалите их, когда вы сделали:

X* x = new X; 
//... 
delete x; //x gets destructed 

2) Вы не можете (по крайней мере, чистым способом). Вы должны указать C++, когда удалять свои объекты, даже если эта команда состоит из конечной скобки. (см. первый фрагмент кода)

3) В C++ нет сборщика мусора. См. Два фрагмента. Вы либо должны явно удалять объекты (если в динамическом хранилище), либо они будут автоматически удалены (но не сборщиком мусора), если они находятся в автоматическом хранилище.

Что-то, что стоит изучить - это умные указатели (там есть множество реализаций), но это тоже не сборщик мусора. Это просто избавляет вас от хлопот управления памятью. Но это не похоже на Java.

+0

+1 для упоминания умных указателей. Существует очень хорошее введение в интеллектуальные указатели [on Stack Overflow] (http://stackoverflow.com/a/106614/1203803). Обратите внимание: стандартная библиотека C++ 11 имеет интеллектуальные указатели, поэтому больше не нужно использовать Boost для этого. –

3

В C++ нет сборщика мусора. Вы должны писать и запускать деструкторы самостоятельно. В C++ распространенная ошибка - забыть запустить деструктор.

Если ваш объект выделен new, вы должны удалить его с помощью delete. Итак, new вызывает contructor, а delete вызывает деструктор.

myclass *p = new myclass(); 
// do something 
delete p; 

Это называется распределением динамических объектов.

Если ваш объект определен как «нормально», он будет автоматически разрушен, если он выходит за рамки.

myclass a; 
// do something 
// will destructed when } 

Это называется автоматическим распределением объектов.

P.S. Вы также не должны назначать нули в Java, поскольку сборщик мусора был изобретен, а именно, чтобы не беспокоиться об удалении объекта.

+1

Можете ли вы изменить «определено нормально» на нечто более формальное? Только вы знаете, что это значит. –

+1

Пробовал. В C++ вы можете создать объект «на куче», который похож на Java. В этом случае вы получаете указатель на объект, который вы должны передать на 'delete' в конце. Другой способ - «нормальный», т. Е. Когда объекты создаются по принципу «по значению», что истинно в Java только для целых типов. –

+3

Куча и стек являются деталями реализации и в этом контексте не являются частью номенклатуры C++. Термины - это автоматическое и динамическое хранилище. Нет никакого «обычного» способа выделения объектов в C++. –

1

C++ использует идиоматику программирования RAII (Инициализация ресурсов), нет ничего похожего на автоматическое управление памятью, известное как Garbage Collector в java или AutoZone в Objective-C 2. Таким образом, надлежащая очистка экземпляра может легко усложниться.Чтобы ответить на ваши вопросы:

ad 1: на C++ нет GC, поэтому вам нужно удалить свои объекты вручную или использовать метод подсчета ссылок или лучше Smart Pointers, которые теперь являются частью стандарта C++ 11, но как насколько я знаю, он еще не доступен ни в одном компиляторе C++. На данный момент вы можете использовать шаблоны Smart Pointer из библиотеки Boost: http://www.boost.org/doc/libs/1_48_0/libs/smart_ptr/smart_ptr.htm. Новый стандарт C++ взял непосредственно реализацию Boost, поэтому в ближайшем будущем не будет проблем при переходе на новый стандарт (MSVC 2012 будет реализовывать поддержку C++ 11).

ad 2: Отсутствует маркировка, просто удалите ее «вручную» в нужном месте или оставьте эту задачу на Умных указателях.

ad 3: Не применимо.

Наконец, всегда есть самый простой вариант - не выделяйте свои объекты в куче, что означает динамически. В Java нет такой возможности, но в C++ есть. Я даже прочитал в некоторых из Stroustrup's (создателя C++) отличные книги по программированию на С ++, которые во время создания C++ такое динамическое распределение не рекомендуется. Он заявил: «Чтобы RAII работал правильно, не должно быть динамического распределения - сегодня это звучит странно, но это то, что написал Страуструп, это не из моей головы, я лично выделяю динамически почти все, как все ...

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

try 
{ 
Class *instance = new Class; 
//some error 
} 
catch(...) 
{ 
//error caught - current execution is terminated immediately, instance is no deleted - memory leak. 
} 

Я Java есть, наконец, утверждение, которое всегда вызывается, так что вы можете выполнить необходимую очистку, когда выброшенное исключение. Но в C++ у вас проблемы ... , если только вы не используете упомянутые умные указатели или какую-то очень похожую технику. При использовании интеллектуальных указателей вам больше не придется беспокоиться о очистке (не совсем верно на практике, но ваша жизнь будет быть определенно проще, а ваш код менее багги).

+0

Основные компиляторы C++ (MSVC, Intel, GCC и Clang) имеют некоторую поддержку для C++ 11, поддержка варьируется от компилятора к компилятору. Новые интеллектуальные указатели широко поддерживаются, потому что они в основном являются расширением библиотеки. VS 2010, gcc еще в 4.3, я думаю, и clang с libC++ все они есть. – bames53

+0

Кроме того, я обычно использую очень мало динамического распределения, предпочитая использовать переменные продолжительности хранения. Я думаю, вы обнаружите, что использование динамического распределения напрямую на C++ намного реже, чем указывает ваш комментарий «распределить динамически почти все, что есть у всех». (и если это не редкость, тогда люди ИМО пишут C++ неправильно). В большинстве случаев, когда можно использовать динамическое распределение напрямую, я предпочитаю использовать тип, который будет обрабатывать его для меня, вместо того, чтобы делать это непосредственно сам, например 'vector' для динамических массивов. – bames53

+0

Спасибо за разъяснение и доработку моего ответа. На самом деле, я знаю о поддержке умных указателей, но не хотел излишне компрометировать свой ответ. И да, я также использую статическое распределение много, возможно, более динамичное. Я прочитал динамическое распределение выглядит более «объективным» и из-за этого злоупотребляют :) Но я также не думаю, что это необходимо для плохой практики. – vitakot

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