С++ Object Instantiation
Я программист C, пытающийся понять С++. Многие учебники демонстрируют создание объектов с помощью фрагмента, например:
Dog* sparky = new Dog();
что подразумевает, что позже вы будете делать:
delete sparky;
что имеет смысл. Теперь, когда нет необходимости в распределении динамической памяти, есть ли причина использовать выше, а не
Dog sparky;
и пусть деструктор вызывается однажды, когда искрящийся выходит из области видимости?
Спасибо!
Ответы
Ответ 1
Наоборот, вы всегда должны отдавать предпочтение распределениям стека, в той мере, в какой это правило, вы никогда не должны иметь новый/удалить в своем пользовательском коде.
Как вы говорите, когда переменная объявлена в стеке, ее деструктор автоматически вызывается, когда он выходит за пределы области действия, что является вашим основным инструментом для отслеживания ресурса ресурса и предотвращения утечек.
В общем, каждый раз, когда вам нужно выделять ресурс, будь то память (путем вызова new), дескрипторы файлов, сокеты или что-то еще, переносите их в класс, где конструктор приобретает ресурс, а деструктор освобождает его, Затем вы можете создать объект такого типа в стеке, и вам гарантированно освободится ваш ресурс, когда он выходит за рамки. Таким образом, вы не должны отслеживать новые пары/удалить все везде, чтобы избежать утечки памяти.
Наиболее распространенное имя для этой идиомы - RAII
Также изучите классы интеллектуальных указателей, которые используются для обертывания результирующих указателей в редких случаях, когда вам нужно выделять что-то новое за пределами выделенного объекта RAII. Вместо этого вы передаете указатель на интеллектуальный указатель, который затем отслеживает его время жизни, например, путем подсчета ссылок, и вызывает деструктор, когда последняя ссылка выходит за пределы области видимости. Стандартная библиотека имеет std::unique_ptr
для простого управления на основе областей и std::shared_ptr
, которая ссылается на подсчет для реализации совместного использования.
Многие обучающие программы демонстрируют объект с помощью фрагмента, такого как...
Итак, вы обнаружили, что большинство учебников сосать.;)
В большинстве руководств учат вас паршивым навыкам на С++, включая вызов new/delete для создания переменных, когда это не необходимо, и давая вам трудное время для отслеживания ваших распределений.
Ответ 2
Хотя наличие вещей в стеке может быть преимуществом с точки зрения распределения и автоматического освобождения вещей, оно имеет некоторые недостатки.
-
Возможно, вам не захочется выделять огромные объекты в стеке.
-
Типы времени выполнения! Рассмотрим этот код:
#include <iostream>
class A {
public:
virtual void f();
};
class B : public A {
public:
virtual void f();
};
void A::f() {cout << "A\n";}
void B::f() {cout << "B\n";}
int main(void) {
A *a = new B();
a->f();
delete a;
return 0;
}
Очевидно, он напечатает "B\n". Теперь давайте посмотрим, что происходит при использовании Stack:
int main(void) {
A a = B();
a->f();
return 0;
}
Он (для С++ ребята obviosly) печатает "A\n". И это не может быть интуитивно понятным для тех, кто похож на Java или другие языки стиля ООП. Причина в том, что у вас больше нет указателя на экземпляре B. Но экземпляр B создается, а затем копируется в экземпляр A (который неявно создается).
К сожалению, это одна из замечательных проблем дизайна С++, на мой взгляд. Многие вещи происходят неинтуитивно. В C у вас есть свои указатели и это. Вы знаете, как их использовать, и они делают ВСЕГДА то же самое. В С++ это не так. Представьте себе, что происходит, когда вы используете в этом примере аргумент для метода - все становится все сложнее, и это имеет огромное значение, если a имеет тип A или *, и если метод является вызовом -значение или вызов по ссылке (возможно много комбинаций, и они [совершенно] ведут себя по-другому).
Ответ 3
Я видел этот анти-шаблон у людей, которые не совсем поняли оператора и адреса. Если им нужно вызвать функцию с указателем, они всегда будут выделяться в куче, чтобы получить указатель.
void FeedTheDog(Dog* hungryDog);
Dog* badDog = new Dog;
FeedTheDog(badDog);
delete badDog;
Dog goodDog;
FeedTheDog(&goodDog);
Ответ 4
Ну, причина использования указателя была бы точно такой же, что и причина использования указателей в C, выделенных с помощью malloc: если вы хотите, чтобы ваш объект жил дольше, чем ваша переменная!
Даже настоятельно рекомендуется НЕ использовать нового оператора, если вы можете его избежать. Особенно, если вы используете исключения. В общем, гораздо безопаснее позволить компилятору освобождать ваши объекты.
Ответ 5
Рассматривайте кучу как очень важную недвижимость и используйте ее очень разумно. Основное правило большого пальца - использовать стек по возможности и использовать кучу всякий раз, когда другого пути нет. Выделяя объекты в стеке, вы можете получить много преимуществ, таких как:
(1). Вам не нужно беспокоиться о стирании стека в случае исключений
(2). Вам не нужно беспокоиться о фрагментации памяти, вызванной распределением большего объема, чем это необходимо вашим менеджером кучи.
Ответ 6
Единственная причина, по которой я буду беспокоиться, это то, что Собака теперь выделена в стеке, а не куча. Поэтому, если у собаки размер мегабайта, у вас может быть проблема,
Если вам нужно перейти на новый маршрут /delete, будьте осторожны с исключениями. И из-за этого вы должны использовать auto_ptr или один из типов интеллектуальных указателей boost для управления временем жизни объекта.
Ответ 7
Нет причин для новых (в куче), когда вы можете выделить в стеке (если по какой-то причине у вас нет небольшого стека и вы хотите использовать кучу.
Возможно, вам захочется использовать shared_ptr (или один из его вариантов) из стандартной библиотеки, если вы хотите выделить в куче. Это будет обрабатывать удаление для вас, как только все ссылки на shared_ptr исчезнут.
Ответ 8
Есть еще одна причина, о которой никто больше не упомянул, почему вы можете создать динамический объект. Динамические объекты на основе кучи позволяют использовать polymorphism.
Ответ 9
У меня была такая же проблема в Visual Studio. Вы должны использовать:
yourClass- > classMethod();
а не:
yourClass.classMethod();