Возможно, этот вопрос уже задавался, но я не смог его найти. Пожалуйста, перенаправьте меня, если вы что-то видели.
Вопрос:
какая польза от использования:
myClass* pointer;
над
myClass* pointer = new(myClass);
Из прочтения других тем я понимаю, что первый параметр выделяет пространство в стеке и делает указатель на него указателем, а второй — на пространство в куче и создает указатель на него.
Но я также читал, что второй вариант утомителен, потому что вы должны освободить место с помощью delete.
Так зачем использовать второй вариант?
Я вроде нуб, поэтому, пожалуйста, объясните подробно.
редактировать
#include <iostream>
using namespace std;
class Dog
{
public:
void bark()
{
cout << "wouf!!!" << endl;
}
};int main()
{
Dog* myDog = new(Dog);
myDog->bark();
delete myDog;
return 0;
}
а также
#include <iostream>
using namespace std;
class Dog
{
public:
void bark()
{
cout << "wouf!!!" << endl;
}
};int main()
{
Dog* myDog;
myDog->bark();
return 0;
}
и скомпилируйте и дайте мне «wouf !!!». Так почему я должен использовать ключевое слово «new»?
Я понимаю, что первый вариант выделяет место в стеке и
указывает на него указатель, а второй выделяет пробел
куча и сделать указатель на него.
Выше указано неверно — первый вариант выделяет место для самого указателя в стеке, но не выделяет место для какого-либо объекта, на который должен указывать указатель. То есть указатель не указывает на что-то конкретное и, следовательно, бесполезен для использования (если только / пока вы не установите указатель на что-то, указывающее)
В частности, это всего лишь слепая удача, что этот код, кажется, «работает» вообще:
Dog* myDog;
myDog->bark(); // ERROR, calls a method on an invalid pointer!
… приведенный выше код вызывает неопределенное поведение, и в идеальном мире он просто потерпит крах, так как вы вызываете метод с неверным указателем. Но компиляторы C ++ обычно предпочитают максимизировать эффективность, а не корректно обрабатывать ошибки программиста, поэтому они обычно не проверяют недействительные указатели, и, поскольку bark()
Метод на самом деле не использует какие-либо данные из Dog
объект, он может выполняться без каких-либо явных сбоев. Попробуйте сделать свой bark()
Виртуальный метод, OTOH, и вы, вероятно, увидите сбой из приведенного выше кода.
вторая выделяет пространство в куче и делает указатель на
Это.
Это правильно.
Но я также читал, что второй вариант утомителен, потому что вы должны
освободить место с помощью delete.
Не только утомительно, но и подвержено ошибкам — очень легко (в нетривиальной программе) найти путь к коду, где вы забыли вызвать delete, и тогда у вас возникает утечка памяти. Или, в качестве альтернативы, вы можете в итоге дважды вызвать delete для одного и того же указателя, и тогда у вас будет неопределенное поведение и, вероятно, сбой или повреждение данных. Ни одна из ошибок не доставляет большого удовольствия.
Так зачем использовать второй вариант?
Традиционно вы будете использовать динамическое размещение, когда вам нужно, чтобы объект оставался действительным дольше, чем область вызывающего кода — например, если вам нужно, чтобы объект оставался неизменным даже после того, как функция, в которой вы создали объект, вернулась. Сравните это с выделением стека:
myClass someStackObject;
… в котором someStackObject
гарантированно уничтожается при возврате вызывающей функции, что, как правило, хорошо, но не при необходимости someStackObject
оставаться в существовании всегда после того, как ваша функция вернулась.
В наши дни большинство людей полностью избегают использования указателей в стиле C /, поскольку они опасно подвержены ошибкам. Современный C ++ способ размещения объекта в куче будет выглядеть так:
std::shared_ptr<myClass> pointer = std::make_shared<myClass>();
… и это предпочтительнее, потому что он дает вам выделенный в куче объект myClass, чей указатель на объект будет продолжать жить до тех пор, пока есть хотя бы один std :: shared_ptr, указывающий на него (хорошо), но также будет автоматически удален в тот момент, когда нет std :: shared_ptr, указывающего на него (даже лучше, так как это означает, что нет утечки памяти и нет необходимости явно вызывать delete
, что означает отсутствие потенциальных двойных удалений)
Других решений пока нет …