Подтвердить что ты не робот

Foo * f = новый Foo хороший код на С++

Чтение через старый журнал С++, который у меня был, я заметил что-то.

В одной из статей утверждалось, что

Foo *f = new Foo();

был почти неприемлемым профессиональным С++-кодом в целом, и было необходимо автоматическое решение для управления памятью.

Это так?

edit: rephrased: прямое управление памятью неприемлемо для нового кода на С++, в общем? Должен ли auto_ptr (или другие обертки управления) использоваться для большинства новых кодов?

4b9b3361

Ответ 1

Этот пример очень похож на Java.
В С++ мы используем только управление динамической памятью, если это необходимо.
Лучшей альтернативой является просто объявление локальной переменной.

{
    Foo    f;

    // use f

} // f goes out of scope and is immediately destroyed here.

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

// In C++14
{
    std::unique_ptr<Foo>  f = std::make_unique<Foo>(); // no need for new anymore
}

// In C++11
{
    std::unique_ptr<Foo>  f(new Foo);  // See Description below.
}

// In C++03
{
    std::auto_ptr<Foo>    f(new Foo);  // the smart pointer f owns the pointer.
                                       // At some point f may give up ownership to another
                                       // object. If not then f will automatically delete
                                       // the pointer when it goes out of scope..

}

Есть целая связка os умных указателей, предоставляемых int std:: и boost:: (теперь некоторые из них находятся в std:: tr1) выбирают подходящую и используют ее для управления продолжительностью жизни вашего объекта.

См. Умные указатели: или кому принадлежит ребенок?

Технически вы можете использовать команду new/delete для управления памятью.
Но в реальном коде на С++ это почти никогда не выполняется. Существует почти всегда лучшая альтернатива управлению памятью вручную.

Простым примером является std::vector. Под обложками он использует новые и удаляет. Но вы никогда не сможете сказать извне. Это полностью прозрачно для пользователя класса. Все, что пользователь знает, это то, что вектор будет владеть объектом, и он будет уничтожен, когда вектор будет уничтожен.

Ответ 2

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

Что касается альтернатив (стековые переменные, интеллектуальные указатели и т.д.), все они имеют свои недостатки. И ни у кого из них нет гибкости, управления прямой памятью. Цена, которую вы должны заплатить за такую ​​гибкость, - это время отладки, и вы должны знать обо всех рисках.

Ответ 3

С помощью какой-либо схемы интеллектуальных указателей вы можете получить автоматическое управление памятью, подсчет ссылок и т.д. с небольшим количеством накладных расходов. Вы платите за это (в памяти или производительности), но, возможно, стоит заплатить за него, а не постоянно беспокоиться об этом.

Ответ 4

Нет.

Есть очень веские причины не использовать автоматические системы управления памятью в определенных случаях. Это может быть производительность, сложность структур данных из-за циклических ссылок и т.д.

Однако я рекомендую использовать только исходный poiner с новым /malloc, если у вас есть веская причина не использовать умение умнее. Наблюдение за незащищенными выделениями пугает меня и заставляет меня надеяться, что кодер знает, что они делают.

Какой-то класс интеллектуальных указателей, например boost:: shared_ptr, boost:: scoped_ptr, станет хорошим началом. (Они будут частью стандарта С++ 0x, поэтому не бойтесь их;))

Ответ 5

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

Ответ 6

Это зависит от того, что мы имеем в виду.

  • Если new никогда не используется для выделения памяти? Конечно, у нас нет другого выбора. new - это способ динамического выделения объектов в С++. Когда нам нужно динамически выделять объект типа T, мы делаем new T(...).
  • Должен ли new вызываться по умолчанию, когда мы хотим создать экземпляр нового объекта? NO. В java или С# new используется для создания новых объектов, поэтому вы используете его везде. в С++ он используется только для распределения кучи. Почти все объекты должны быть выделены в стек (или созданы на месте как члены класса), чтобы правила определения языка помогали нам управлять их сроками службы. new не часто требуется. Обычно, когда мы хотим выделить новые объекты в куче, вы делаете это как часть более крупной коллекции, и в этом случае вам нужно просто нажать объект на свой контейнер STL и позволить ему беспокоиться о распределении и освобождении памяти. Если вам нужен только один объект, его обычно можно создать как член класса или локальную переменную без использования new.
  • Должен ли new присутствовать в коде бизнес-логики? Редко, если когда-либо. Как упоминалось выше, он может и должен быть обычно скрыт внутри классов оболочки. std::vector, например, динамически выделяет требуемую память. Поэтому пользователю vector не нужно заботиться. Я просто создаю вектор в стеке, и он заботится о распределении кучи для меня. Если векторный или другой класс контейнера не подходит, мы можем захотеть написать нашу собственную оболочку RAII, которая выделяет некоторую память в конструкторе с помощью new и освобождает ее в деструкторе. И эта оболочка может быть распределена по стекам, поэтому пользователю класса никогда не нужно вызывать new.

Одна из статей утверждала, что Foo *f = new Foo(); был почти неприемлемым профессиональным С++-кодом в целом, и было необходимо решение для автоматической управления памятью.

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

Вместо Foo *f = new Foo();, вы должны использовать один из них:

Scoped_Foo f; // just create a wrapper which *internally* allocates what it needs on the heap and frees it when it goes out of scope
shared_ptr<Foo> f = new Foo(); // if you *do* need to dynamically allocate an object, place the resulting pointer inside a smart pointer of some sort. Depending on circumstances, scoped_ptr, or auto_ptr may be preferable. Or in C++0x, unique_ptr
std::vector<Foo> v; v.push_back(Foo()); // place the object in a vector or another container, and let that worry about memory allocations.

Ответ 7

Я прекратил писать такой код некоторое время назад. Существует несколько альтернатив:

Удаление на основе области

{
    Foo foo;
    // done with foo, release
}

scoped_ptr для динамического распределения по размеру

{
    scoped_ptr<Foo> foo( new Foo() );
    // done with foo, release
}

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

shared_ptr<Foo> foo;
{ 
    foo.reset( new Foo() );
} 
// still alive
shared_ptr<Foo> bar = foo; // pointer copy
...
foo.reset(); // Foo still lives via bar
bar.reset(); // released

Управление ресурсами на основе факторинга

Foo* foo = fooFactory.build();
...
fooFactory.release( foo ); // or it will be 
                           // automatically released 
                           // on factory destruction

Ответ 8

В общем случае нет, но общий случай - не обычный случай. Именно поэтому были изобретены автоматические схемы, такие как RAII.

Из ответа я написал еще один вопрос:

Задача программиста - выразить вещи элегантно на его языке выбор.

С++ имеет очень хорошую семантику для строительства и уничтожения объектов в стеке. Если ресурс могут быть выделены на время блок видимости, затем хороший программист вероятно, займет тот путь наименее сопротивление. Время жизни объекта ограниченные фигурными скобками, которые, вероятно, уже там.

Если нет хорошего способа поставить объект непосредственно в стеке, возможно, это могут быть помещены в другой объект как член. Теперь его жизнь немного дольше, но С++ все еще много автоматически. Время жизни объекта ограничивается родительским объектом - проблема делегирована.

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

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

Наконец, функции записи могут быть жесткий. Возможно, правила, регулирующие объект займет слишком много времени и память, чтобы на самом деле вычислить! И это может быть просто трудно выразить они элегантно, возвращаясь к моему оригинальный пункт. Поэтому для этого у нас есть сбор мусора: объект время жизни ограничено, когда вы хотите это и когда вы этого не сделаете.

Ответ 9

Прежде всего, я считаю, что это должно быть Foo *f = new Foo();

И причина, по которой мне не нравится использовать этот синтаксис, потому что легко забыть добавить delete в конце кода и оставить память a-leakin '.

Ответ 10

В целом ваш пример не является безопасным для исключений и поэтому не должен использоваться. Если строка непосредственно следует за новыми бросками? Стек распадается, и вы только что просочились в память. Умный указатель позаботится об этом для вас, поскольку часть стека раскрутится. Если вы склонны не обрабатывать исключения, тогда нет обратного пути за пределами проблем RAII.