когда я должен использовать новый оператор в C ++ - PullRequest
2 голосов
/ 22 февраля 2011

Скажем, у меня есть класс с именем Money, который имеет параметры Dollars и Cents

Я мог бы инициализировать его двумя способами:

  1. Деньги а (3,15);
  2. Деньги * b = Новые деньги (3,15);

Мой вопрос: когда я должен использовать (1) и когда я должен использовать (2)

Ответы [ 9 ]

8 голосов
/ 22 февраля 2011

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

Помимо этого, (к сожалению) не существует действительно жестких и быстрых инструкций, которым можно следовать, которые гарантируют, что вы делаете все как можно лучше.

6 голосов
/ 22 февраля 2011

Первый создает объект Money в стеке, срок его службы находится в пределах того времени, когда он был создан.Это означает, что когда вы нажимаете }, он выходит из области видимости и возвращается память.Используйте это, когда вы хотите создать объект внутри одной функции.

Второй создает объект Money в куче, его срок жизни такой же, как вы этого хотите, а именно до тех пор, пока вы deleteЭто.Используйте это, когда вы хотите, чтобы ваш объект передавался различным функциям

3 голосов
/ 22 февраля 2011
Money a(3,15);

Выделяет объект Money в локальной области.

Money* b=new Money(3,15);

Распределяет указатель-переменную в локальной области и делает указатель «точкой» для Money объекта, который находится в свободном хранилище (при условии, что выделение завершено успешно, в противном случае std::bad_alloc() генерируется)

Пример 1:

Допустим следующий сценарий:

Money * b = initialize();

, где

Money* initialize()
{
      Money x(2 , 15);
      return &x;
}

Это не удастся, потому что после того, как initialize() достигнет конца выполнения, x будет уничтожено, а теперь b указывает на местоположение, которое недопустимо для использования, и вызывает Undefined Behavior, если вы его использовали. так что вместо этого вы должны выделить его указателем

Money* initialize()
{
      return new Money(2,15);
}

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

Существует разница между ними, как вы заметили в примере, и в том, что в локальной области действия x вам не нужно delete объект. Но при использовании new вам придется вручную сделать delete x;. В противном случае происходит утечка памяти (пространство памяти занято, и никогда больше не будет использоваться, следовательно, память используется).

См. Ответ Мартина Йорка для более глубоких знаний, помимо этого поста.

2 голосов
/ 22 февраля 2011

Это гораздо более сложный вопрос, чем кажется.Простой ответ:

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

Будьте осторожны, чтобы не передать указатель на один из этих объектов, ограниченных областью действия, вверх по стеку вызовов (возврат, выходные параметры), это простой способ segfault.

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

Посмотрите на shared_ptr, scoped_ptr, auto_ptr и др.для некоторых альтернатив, которые # 2 действуют в некотором роде, например # 1.

Кроме того, взгляните на этот вопрос для некоторых указателей по управлению памятью в C ++.

1 голос
/ 22 февраля 2011

Форма 1 самая простая; используйте его, когда можете.

Форма 2 покупает вам следующие вещи:

  • Возможность определить во время выполнения, создавать ли объект вообще.
  • Возможность определения во время выполнения, насколько велик массив этих объектов для создания
  • Возможность определить во время выполнения, какой подкласс создать (например, должен ли Money* b указывать на GoodMoney или BadMoney
  • Возможность определять во время выполнения жизненный цикл объекта

Форма 2 вводит возможность или утечку ресурсов, поскольку объекты, созданные с помощью new, должны быть уничтожены с помощью delete. Как уже отмечали другие, эту проблему можно устранить или смягчить с помощью интеллектуальных указателей.

Короче говоря, используйте Форму 2, когда вам нужна одна из перечисленных выше вещей, а затем поместите ее в умный указатель; в противном случае используйте Форму 1.

1 голос
/ 22 февраля 2011

Технически, вы бы предпочли, чтобы вы никогда не делали (2) напрямую, но предпочитали использовать умный указатель:

std::auto_ptr<Money> b(new Money(3,15));  // auto_ptr is just an example of a smart pointer

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

  1. Считается объектом автоматического хранения.Это означает, что он автоматически создается и уничтожается (важный бит) кодом, сгенерированным компилятором.

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

Если ваш объект дешев, создать копию (как она выглядит).Тогда вам вряд ли когда-нибудь понадобится создавать объект динамически.Передача объекта в функцию или возвращение результата могут быть выполнены вполне нормально:

Money CalcInterest(Money const& m, double interest)
{
    Money result(m.doallas * interest, m.cent * interest);
    return result; // Return a copy quite happily.
}

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

struct Expression
{
    char   op;
    std::auto_ptr<Money>   lhs;
    std::auto_ptr<Money>   rhs;
};

std::auto_ptr<Expression> getExpressionFromUserInput()
{
     std::auto_ptr<Expression>  result(new Expressions(/* etc */);
     return result;
}
0 голосов
/ 22 февраля 2011

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

int someFunc() {
    Money a(3,15);
    ...
} // At this point, Money is destroyed and memory is freed.

В качестве альтернативы, если вы хотите, чтобы объекты оставались в живых после функции, вы должны использовать новое следующим образом:

Money *someOtherFunc() {
    Money *a = new Money(3,15);
    ....
    return a;
} // we have to return a here or delete it before the return or we leak that memory.

Надеюсь, это поможет.

0 голосов
/ 22 февраля 2011

Это совершенно другое.

  1. У вас есть объект, который построен в стеке. Это будет иметь срок службы, который длится для блока кода.

  2. У вас есть объект, инициализированный по некоторому адресу памяти, выделенному в куче. Он не будет уничтожен, пока вы не позвоните delete b.

0 голосов
/ 22 февраля 2011

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

...