Создание Экземпляра Объекта C++
Я программист C пытается понять C++. Многие учебники демонстрируют создание экземпляра объекта с помощью фрагмента кода, например:
Dog* sparky = new Dog();
что означает, что позже вы будете делать:
delete sparky;
в этом есть смысл. Теперь, в случае, когда динамическое выделение памяти не требуется, есть ли причина использовать выше вместо
Dog sparky;
и пусть деструктор будет вызван, как только спарки выходит из области видимости?
спасибо!
9 ответов:
напротив, вы всегда должны отдавать предпочтение распределению стека, поскольку, как правило, вы никогда не должны иметь new/delete в своем пользовательском коде.
Так что в общем случае, каждый раз, когда вам нужно выделить ресурс, будь то память (путем вызова new), файл дескрипторы, сокеты или что-нибудь еще, оберните его в класс, где конструктор получает ресурс, а деструктор освобождает его. Затем вы можете создать объект этого типа в стеке, и вы гарантируете, что ваш ресурс будет освобожден, когда он выйдет за пределы области. Таким образом, вам не нужно отслеживать Ваши новые/удаленные пары везде, чтобы избежать утечек памяти.
наиболее распространенным именем для этой идиомы является RAII
также посмотрите на классы интеллектуальных указателей которые используются для обертывания результирующих указателей в редких случаях, когда вам нужно выделить что-то новое вне выделенного объекта RAII. Вместо этого вы передаете указатель на смарт-указатель, который затем отслеживает его жизни, например, путем подсчета ссылок, и вызывает деструктор, когда последняя ссылка выходит из области видимости. Стандартная библиотека
std::unique_ptr
для простого управления на основе области, иstd::shared_ptr
который делает подсчет ссылок для реализации совместного владения.многие учебники демонстрируют объекта создание экземпляра с помощью фрагмента кода, например ...
Так что вы обнаружили, что большинство учебников сосать. ;) Большинство учебников учат вас паршивым практикам C++, включая вызов new/delete для создания переменных, когда это не нужно, и дает вам трудное время для отслеживания времени жизни ваших распределений.
хотя наличие вещей в стеке может быть преимуществом с точки зрения распределения и автоматического освобождения, у него есть некоторые недостатки.
вы не можете выделить огромные объекты в стеке.
динамический отправка! Рассмотрим этот код:
#include <iostream> class A { public: virtual void f(); virtual ~A() {} }; class B : public A { public: virtual void f(); }; void A::f() {cout << "A";} void B::f() {cout << "B";} int main(void) { A *a = new B(); a->f(); delete a; return 0; }
это будет печатать "B". Теперь давайте посмотрим, что происходит при использовании стека:
int main(void) { A a = B(); a.f(); return 0; }
это будет печатать "A", который не может быть интуитивно понятный для тех, кто знаком с Java и других объектно-ориентированных языков. Причина в том, что у вас нет указателя на экземпляр
B
больше. Вместо этого, экземплярB
создается и копируется вa
переменной типаA
.некоторые вещи могут произойти неумышленно, особенно когда вы новичок в C++. В C у вас есть указатели, и это все. Вы знаете, как их использовать, и они всегда делают то же самое. В C++ это не так. Только представьте, что бывает, когда вы используете a в этом примере в качестве аргумента для метода-все становится сложнее, и это имеет огромное значение, если
a
типаA
илиA*
или дажеA&
(вызов по значению). Возможны многие комбинации, и все они ведут себя по-разному.
ну, причина использования указателя будет точно такой же, что и причина использования указателей в C, выделенных с помощью malloc: если вы хотите, чтобы ваш объект жил дольше, чем ваша переменная!
даже настоятельно рекомендуется не использовать новый оператор, если вы можете его избежать. Особенно если вы используете исключения. В общем, гораздо безопаснее позволить компилятору освободить ваши объекты.
Я видел этот анти-шаблон от людей, которые не совсем получают & адрес оператора. Если им нужно вызвать функцию с указателем, они всегда будут выделять в куче, чтобы получить указатель.
void FeedTheDog(Dog* hungryDog); Dog* badDog = new Dog; FeedTheDog(badDog); delete badDog; Dog goodDog; FeedTheDog(&goodDog);
относитесь к куче как к очень важной недвижимости и используйте ее очень разумно. Основное правило большого пальца заключается в том, чтобы использовать стек по возможности и использовать кучу всякий раз, когда нет другого способа. Выделяя объекты в стеке, вы можете получить множество преимуществ, таких как:
(1). Вам не нужно беспокоиться о разматывании стека в случае исключений
(2). Вам не нужно беспокоиться о фрагментации памяти, вызванной выделением большего пространства, чем необходимо для вашей кучи менеджер.
единственная причина, по которой я бы беспокоился о том, что собака теперь выделяется в стеке, а не в куче. Так что если собака мегабайт в размере, у вас может быть проблема,
Если вам нужно перейти на новый / удалить маршрут, будьте осторожны с исключениями. И из-за этого вы должны использовать auto_ptr или один из типов интеллектуальных указателей boost для управления временем жизни объекта.
нет причин для нового (в куче), когда вы можете выделить в стеке (если по какой-то причине у вас есть небольшой стек и вы хотите использовать кучу.
вы можете рассмотреть возможность использования shared_ptr (или одного из его вариантов) из стандартной библиотеки, если вы хотите выделить в куче. Это будет обрабатывать делать удаление для вас, как только все ссылки на shared_ptr вышли из существования.
есть еще одна причина, о которой никто еще не упоминал, почему вы можете создать свой объект динамически. Динамические объекты на основе кучи позволяют использовать полиморфизм.