С++ 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();