Создание Экземпляра Объекта C++


Я программист C пытается понять C++. Многие учебники демонстрируют создание экземпляра объекта с помощью фрагмента кода, например:

Dog* sparky = new Dog();

что означает, что позже вы будете делать:

delete sparky;

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

Dog sparky;

и пусть деструктор будет вызван, как только спарки выходит из области видимости?

спасибо!

9 108

9 ответов:

напротив, вы всегда должны отдавать предпочтение распределению стека, поскольку, как правило, вы никогда не должны иметь new/delete в своем пользовательском коде.

Так что в общем случае, каждый раз, когда вам нужно выделить ресурс, будь то память (путем вызова new), файл дескрипторы, сокеты или что-нибудь еще, оберните его в класс, где конструктор получает ресурс, а деструктор освобождает его. Затем вы можете создать объект этого типа в стеке, и вы гарантируете, что ваш ресурс будет освобожден, когда он выйдет за пределы области. Таким образом, вам не нужно отслеживать Ваши новые/удаленные пары везде, чтобы избежать утечек памяти.

наиболее распространенным именем для этой идиомы является RAII

также посмотрите на классы интеллектуальных указателей которые используются для обертывания результирующих указателей в редких случаях, когда вам нужно выделить что-то новое вне выделенного объекта RAII. Вместо этого вы передаете указатель на смарт-указатель, который затем отслеживает его жизни, например, путем подсчета ссылок, и вызывает деструктор, когда последняя ссылка выходит из области видимости. Стандартная библиотека std::unique_ptr для простого управления на основе области, и std::shared_ptr который делает подсчет ссылок для реализации совместного владения.

многие учебники демонстрируют объекта создание экземпляра с помощью фрагмента кода, например ...

Так что вы обнаружили, что большинство учебников сосать. ;) Большинство учебников учат вас паршивым практикам C++, включая вызов new/delete для создания переменных, когда это не нужно, и дает вам трудное время для отслеживания времени жизни ваших распределений.

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

  1. вы не можете выделить огромные объекты в стеке.

  2. динамический отправка! Рассмотрим этот код:

#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 вышли из существования.

есть еще одна причина, о которой никто еще не упоминал, почему вы можете создать свой объект динамически. Динамические объекты на основе кучи позволяют использовать полиморфизм.

У меня была такая же проблема в Visual Studio. Вы должны использовать:

yourClass - >classMethod ();

вместо:

ваш класс.classMethod ();