Почему я должен использовать указатель, а не сам объект?

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

Object *myObject = new Object; 

скорее, чем:

 Object myObject; 

Или вместо использования функции, скажем, testFunc() , вот так:

 myObject.testFunc(); 

мы должны написать:

 myObject->testFunc(); 

Но я не могу понять, почему мы должны это делать. Я бы предположил, что это связано с эффективностью и скоростью, поскольку мы получаем прямой доступ к адресу памяти. Я прав?

Очень жаль, что вы так часто видите динамическое распределение. Это просто показывает, сколько плохих программистов на C ++ существует.

В некотором смысле у вас есть два вопроса, связанных в один. Во-первых, когда следует использовать динамическое распределение (используя new )? Во-вторых, когда мы должны использовать указатели?

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

Динамическое распределение

В вашем вопросе вы продемонстрировали два способа создания объекта. Основное различие заключается в длительности хранения объекта. При выполнении Object myObject; внутри блока объект создается с автоматическим временем хранения, что означает, что он будет автоматически уничтожен, когда он выходит за frameworks. Когда вы выполняете new Object() , объект имеет динамическую продолжительность хранения, что означает, что он остается живым, пока вы явно не delete его. При необходимости вы должны использовать только динамическую память. То есть, вы всегда должны предпочитать создание объектов с автоматическим временем хранения, когда сможете .

Основные две ситуации, в которых вам может потребоваться динамическое распределение:

  1. Вам нужен объект, чтобы пережить текущую область действия – этот конкретный объект в этой конкретной ячейке памяти, а не ее копию. Если вы согласны с копированием / перемещением объекта (большую часть времени вы должны быть), вам следует выбрать автоматический объект.
  2. Вам нужно выделить много памяти , которая может легко заполнить стек. Было бы неплохо, если бы нам не приходилось относиться к этому (большую часть времени вам не нужно), поскольку это действительно вне сферы компетенции C ++, но, к сожалению, нам приходится иметь дело с реальностью систем, которые мы для развивающихся.

Когда вам действительно требуется динамическое распределение, вы должны инкапсулировать его в интеллектуальный указатель или какой-либо другой тип, который выполняет RAII (например, стандартные контейнеры). Умные указатели предоставляют семантику владения динамически распределенных объектов. Посмотрите, например, на std::unique_ptr и std::shared_ptr . Если вы используете их надлежащим образом, вы почти полностью можете избежать выполнения собственного управления памятью (см. Правило «Нуль» ).

указатели

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

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

  2. Вам нужен polymorphism . Полиморфно (то есть в соответствии с динамическим типом объекта) вы можете вызывать функции только через указатель или ссылку на объект. Если это поведение вам нужно, вам нужно использовать указатели или ссылки. Опять же, ссылки должны быть предпочтительными.

  3. Вы хотите представить, что объект является необязательным , позволяя передавать nullptr , когда объект опущен. Если это аргумент, вы должны предпочесть использовать аргументы по умолчанию или функции перегрузки. В противном случае вам следует использовать тип, который инкапсулирует это поведение, например std::optional (введенный на C ++ 17 – с более ранними стандартами C ++, используйте boost::optional ).

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

  5. Вам необходимо взаимодействовать с библиотекой C или библиотекой C-стиля. На данный момент вы вынуждены использовать необработанные указатели. Лучшее, что вы можете сделать, это убедиться, что вы позволите своим сырым указателям освободиться в последний момент. Вы можете получить необработанный указатель из умного указателя, например, используя функцию get member. Если библиотека выполняет какое-то выделение для вас, которое ожидает от вас освобождения с помощью дескриптора, вы можете часто обернуть дескриптор в интеллектуальном указателе с помощью настраиваемого делетера, который соответствующим образом освободит объект.

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

Полиморфное поведение . Для полиморфных типов указатели (или ссылки) используются для исключения нарезки:

 class Base { ... }; class Derived : public Base { ... }; void fun(Base b) { ... } void gun(Base* b) { ... } void hun(Base& b) { ... } Derived d; fun(d); // oops, all Derived parts silently "sliced" off gun(&d); // OK, a Derived object IS-A Base object hun(d); // also OK, reference also doesn't slice 

Ссылочная семантика и избежание копирования . Для неполиморфных типов указатель (или ссылка) избегает копирования потенциально дорогого объекта

 Base b; fun(b); // copies b, potentially expensive gun(&b); // takes a pointer to b, no copying hun(b); // regular syntax, behaves as a pointer 

Обратите внимание, что C ++ 11 имеет семантику перемещения, которая позволяет избежать многих копий дорогостоящих объектов в аргумент функции и в качестве возвращаемых значений. Но использование указателя, безусловно, позволит избежать этого и позволит использовать несколько указателей на одном и том же объекте (тогда как объект может перемещаться только один раз).

Сбор ресурсов . Создание указателя на ресурс с использованием new оператора – это анти-шаблон в современном C ++. Используйте специальный class ресурсов (один из стандартных контейнеров) или интеллектуальный указатель ( std::unique_ptr<> или std::shared_ptr<> ). Рассматривать:

 { auto b = new Base; ... // oops, if an exception is thrown, destructor not called! delete b; } 

против

 { auto b = std::make_unique(); ... // OK, now exception safe } 

Необработанный указатель должен использоваться только как «вид» и никоим образом не связан с владением, будь то прямое создание или неявно через возвращаемые значения. См. Также этот вопрос и ответы на часто задаваемые вопросы по C ++ .

Более мелкозернистый контроль времени жизни Каждый раз, когда копируется общий указатель (например, как аргумент функции), ресурс, на который он указывает, сохраняется в живых. Обычные объекты (не созданные new , либо непосредственно вами, либо внутри classа ресурсов) уничтожаются при выходе из области видимости.

Есть много отличных ответов на этот вопрос, в том числе важные варианты использования форвардных деклараций, polymorphismа и т. Д., Но я чувствую, что часть «души» вашего вопроса не отвечает, а именно, что означают разные синтаксисы для Java и C ++.

Давайте рассмотрим ситуацию, сравнивающую два языка:

Ява:

 Object object1 = new Object(); //A new object is allocated by Java Object object2 = new Object(); //Another new object is allocated by Java object1 = object2; //object1 now points to the object originally allocated for object2 //The object originally allocated for object1 is now "dead" - nothing points to it, so it //will be reclaimed by the Garbage Collector. //If either object1 or object2 is changed, the change will be reflected to the other 

Ближайшим эквивалентом этого является:

C ++:

 Object * object1 = new Object(); //A new object is allocated on the heap Object * object2 = new Object(); //Another new object is allocated on the heap delete object1; //Since C++ does not have a garbage collector, if we don't do that, the next line would //cause a "memory leak", ie a piece of claimed memory that the app cannot use //and that we have no way to reclaim... object1 = object2; //Same as Java, object1 points to object2. 

Давайте посмотрим альтернативный способ C ++:

 Object object1; //A new object is allocated on the STACK Object object2; //Another new object is allocated on the STACK object1 = object2;//!!!! This is different! The CONTENTS of object2 are COPIED onto object1, //using the "copy assignment operator", the definition of operator =. //But, the two objects are still different. Change one, the other remains unchanged. //Also, the objects get automatically destroyed once the function returns... 

Лучший способ подумать о том, что – более или менее – Java (неявно) обрабатывает указатели на объекты, а C ++ может обрабатывать либо указатели на объекты, либо сами объекты. Есть исключения из этого – например, если вы объявляете «примитивные» типы Java, это фактические значения, которые копируются, а не указатели. Так,

Ява:

 int object1; //An integer is allocated on the stack. int object2; //Another integer is allocated on the stack. object1 = object2; //The value of object2 is copied to object1. 

Тем не менее, использование указателей НЕ обязательно является правильным или неправильным способом обработки вещей; однако другие ответы покрывали это удовлетворительно. Общая идея заключается в том, что в C ++ у вас гораздо больше контроля над временем жизни объектов и от того, где они будут жить.

Возьмите главную точку – Object * object = new Object() – это фактически то, что ближе всего к семантике типичной Java (или C #).

Еще одна веская причина использовать указатели – для форвардных деклараций . В достаточно большом проекте они могут ускорить время компиляции.

Предисловие

Java не похож на C ++, вопреки шуму. Машина hype для Java хотела бы, чтобы вы поверили, что, поскольку Java имеет синтаксис C ++, аналогичные языки. Ничто не может быть дальше от истины. Эта дезинформация является частью причины, по которой Java-программисты переходят на C ++ и используют синтаксис, подобный Java, без понимания последствий их кода.

Вперед мы идем

Но я не могу понять, почему мы должны это делать. Я бы предположил, что это связано с эффективностью и скоростью, поскольку мы получаем прямой доступ к адресу памяти. Я прав?

Напротив, на самом деле. Куча намного медленнее, чем стек, потому что стек очень прост по сравнению с кучей. Автоматические переменные хранилища (ака стековые переменные) вызывают их деструкторы, когда они выходят за frameworks. Например:

 { std::string s; } // s is destroyed here 

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

 { std::string* s = new std::string; } delete s; // destructor called 

Это не имеет ничего общего с new синтаксисом, распространенным в C # и Java. Они используются для совершенно разных целей.

Преимущества динамического распределения

1. Вам не нужно заранее знать размер массива

Одна из первых проблем, с которыми сталкиваются многие программисты на С ++, заключается в том, что, когда они принимают произвольный вход от пользователей, вы можете выделить фиксированный размер для переменной стека. Вы не можете resize массивов. Например:

 char buffer[100]; std::cin >> buffer; // bad input = buffer overflow 

Конечно, если вы использовали std::string вместо этого, std::string внутренне меняет размеры, поэтому это не должно быть проблемой. Но по существу решение этой проблемы – динамическое распределение. Вы можете выделить динамическую память на основе ввода пользователя, например:

 int * pointer; std::cout << "How many items do you need?"; std::cin >> n; pointer = new int[n]; 

Сторона примечания : Одна ошибка, которую делают многие новички, – использование массивов переменной длины. Это расширение GNU, а также одно в Clang, потому что они отражают многие расширения GCC. Поэтому нельзя полагаться на следующий int arr[n] .

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

2. Массивы не являются указателями

Как вы это посоветовали? Ответ станет ясным, как только вы поймете путаницу / миф за массивами и указателями. Обычно считается, что они одинаковы, но это не так. Этот миф исходит из того факта, что указатели могут быть индексированы так же, как массивы, и из-за разложения массивов на указатели на верхнем уровне в объявлении функции. Однако, как только массив распадается на указатель, указатель теряет свой sizeof информации. Таким образом, sizeof(pointer) даст размер указателя в байтах, который обычно составляет 8 байтов в 64-битной системе.

Вы не можете назначать массивы, только инициализировать их. Например:

 int arr[5] = {1, 2, 3, 4, 5}; // initialization int arr[] = {1, 2, 3, 4, 5}; // The standard dictates that the size of the array // be given by the amount of members in the initializer arr = { 1, 2, 3, 4, 5 }; // ERROR 

С другой стороны, вы можете делать все, что хотите, с помощью указателей. К сожалению, поскольку различие между указателями и массивами размахивается armми на Java и C #, новички не понимают разницы.

3. Полиморфизм

Java и C # имеют средства, позволяющие рассматривать объекты как другие, например, используя ключевое слово as . Поэтому, если кто-то хотел рассматривать объект Entity как объект Player , можно было бы использовать Player player = Entity as Player; Это очень полезно, если вы намерены вызывать функции на однородном контейнере, которые должны применяться только к определенному типу. Функциональность может быть достигнута следующим образом:

 std::vector vector; vector.push_back(&square); vector.push_back(&triangle); for (auto& e : vector) { auto test = dynamic_cast(e); // I only care about triangles if (!test) // not a triangle e.GenericFunction(); else e.TriangleOnlyMagic(); } 

Скажем, если бы только треугольники имели функцию Rotate, это была бы ошибка компилятора, если бы вы попытались вызвать ее на всех объектах classа. Используя dynamic_cast , вы можете имитировать ключевое слово as . Чтобы быть ясным, если сбой выполняется, он возвращает недопустимый указатель. Таким образом, !test по существу является сокращением для проверки того, является ли test NULL или недопустимым указателем, что означает, что приведение не выполнено.

Преимущества автоматических переменных

Увидев все великие вещи, которые может сделать динамическое распределение, вы, вероятно, задаетесь вопросом, почему никто не будет использовать динамическое распределение все время? Я уже сказал вам одну причину: куча медленная. И если вам не нужна вся эта память, вы не должны злоупотреблять ею. Итак, вот некоторые недостатки в каком-то конкретном порядке:

  • Он подвержен ошибкам. Распределение памяти вручную опасно, и вы склонны к утечкам. Если вы не умеете использовать отладчик или valgrind (средство утечки памяти), вы можете вытащить свои волосы из головы. К счастью, идиомы RAII и умные указатели немного облегчают это, но вы должны быть знакомы с такими практиками, как «Правило из трех» и «Правило пяти». Вниманию много информации, и новички, которые либо не знают, либо не заботятся, попадают в эту ловушку.

  • Это не обязательно. В отличие от Java и C #, где идиоматично использовать new ключевое слово везде, на C ++, вы должны использовать его только в случае необходимости. Обычная фраза идет, все выглядит как гвоздь, если у вас есть молоток. В то время как начинающие, начинающие с C ++, боятся указателей и учатся использовать переменные стека по привычке, программисты Java и C # начинают с использования указателей, не понимая этого! Это буквально отступает на неправильную ногу. Вы должны отказаться от всего, что знаете, потому что синтаксис – это одно, а изучение языка – другое.

1. (N) RVO – Aka, (Именованный) Оптимизация возвращаемого значения

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

Если вы используете указатели, (N) RVO НЕ происходит. Более выгодно и менее подвержено ошибкам использовать (N) RVO, а не возвращать или пропускать указатели, если вас беспокоит оптимизация. Ошибка утечки может произойти, если вызывающая функция отвечает за delete динамически выделенного объекта и т. Д. Трудно отследить право собственности на объект, если указатели передаются как горячий картофель. Просто используйте переменные стека, потому что это проще и лучше.

C ++ дает вам три способа передать объект: по указателю, по ссылке и по значению. Java ограничивает вас последним (единственное исключение – это примитивные типы, такие как int, boolean и т. Д.). Если вы хотите использовать C ++ не просто как странную игрушку, тогда вам лучше узнать разницу между этими тремя способами.

Java притворяется, что нет такой проблемы, как «кто и когда должен ее уничтожить?». Ответ: сборщик мусора, большой и ужасный. Тем не менее, он не может обеспечить 100% защиту от утечек памяти (да, java может утечка памяти ). На самом деле, GC дает вам ложное чувство безопасности. Чем больше ваш внедорожник, тем длиннее ваш путь к эвакуатору.

C ++ оставляет вас лицом к лицу с управлением жизненным циклом объекта. Ну, есть способы справиться с этим (семейство интеллектуальных указателей , QObject в Qt и т. Д.), Но ни один из них не может использоваться в режиме «огонь и забыть», как GC: вы всегда должны иметь в виду обработку памяти. Вы должны не только заботиться об уничтожении объекта, но и избегать уничтожения одного и того же объекта более одного раза.

Не испугались? Хорошо: циклические ссылки – обрабатывайте их самостоятельно, человек. И помните: убивайте каждый объект ровно один раз, мы C ++ runtimes не любят тех, кто возится с трупами, оставляют мертвых в одиночку.

Итак, вернемся к вашему вопросу.

Когда вы передаете свой объект по значению, а не по указателю или по ссылке, вы копируете объект (весь объект, будь то пара байтов или огромный дамп базы данных), вы достаточно умны, чтобы избежать последнего, вы?) каждый раз, когда вы делаете «=». И чтобы получить доступ к членам объекта, вы используете ‘.’ (Точка).

Когда вы передаете свой объект по указателю, вы копируете только несколько байтов (4 в 32-битных системах, 8 на 64-битных), а именно – адрес этого объекта. И чтобы показать это всем, вы используете этот причудливый оператор «->» при доступе к членам. Или вы можете использовать комбинацию ‘*’ и ‘.’.

Когда вы используете ссылки, вы получаете указатель, который претендует на значение. Это указатель, но вы получаете доступ к членам через «.».

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

  • Тип предоставляется всем
  • Модификатор значения / указателя / ссылки является индивидуальным

Пример:

 struct MyStruct { int* someIntPointer, someInt; //here comes the surprise MyStruct *somePointer; MyStruct &someReference; }; MyStruct s1; //we allocated an object on stack, not in heap s1.someInt = 1; //someInt is of type 'int', not 'int*' - value/pointer modifier is individual s1.someIntPointer = &s1.someInt; *s1.someIntPointer = 2; //now s1.someInt has value '2' s1.somePointer = &s1; s1.someReference = s1; //note there is no '&' operator: reference tries to look like value s1.somePointer->someInt = 3; //now s1.someInt has value '3' *(s1.somePointer).someInt = 3; //same as above line *s1.somePointer->someIntPointer = 4; //now s1.someInt has value '4' s1.someReference.someInt = 5; //now s1.someInt has value '5' //although someReference is not value, it's members are accessed through '.' MyStruct s2 = s1; //'NO WAY' the compiler will say. Go define your '=' operator and come back. //OK, assume we have '=' defined in MyStruct s2.someInt = 0; //s2.someInt == 0, but s1.someInt is still 5 - it's two completely different objects, not the references to the same one 

В C ++ объекты, выделенные в стеке (с использованием Object object; оператор внутри блока), будут жить только в пределах области, в которой они объявлены. Когда блок кода завершает выполнение, объявленный объект уничтожается. Если вы выделяете память на кучу, используя Object* obj = new Object() , они продолжают жить в куче до тех пор, пока вы не назовете delete obj .

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

Но я не могу понять, почему мы должны использовать его так?

Я буду сравнивать, как он работает внутри тела функции, если вы используете:

 Object myObject; 

Внутри функции ваш myObject будет уничтожен после возвращения этой функции. Поэтому это полезно, если вам не нужен ваш объект вне вашей функции. Этот объект будет помещен в текущий стек streamа.

Если вы пишете внутри тела функции:

  Object *myObject = new Object; 

то экземпляр classа Object, указанный myObject , не будет уничтожен после завершения функции, а выделение находится в куче.

Теперь, если вы программист Java, то второй пример ближе к тому, как распределение объектов работает под java. Эта строка: Object *myObject = new Object; эквивалентно java: Object myObject = new Object(); , Разница в том, что в java myObject будет получен garbage collection, а в c ++ он не будет освобожден, вы должны где-то явно вызвать `delete myObject; ‘ в противном случае вы будете вводить утечки памяти.

Начиная с c ++ 11 вы можете использовать безопасные способы динамических распределений: new Object , сохраняя значения в shared_ptr / unique_ptr.

 std::shared_ptr safe_str = make_shared("make_shared"); // since c++14 std::unique_ptr safe_str = make_unique("make_shared"); 

Кроме того, объекты очень часто хранятся в контейнерах, таких как map-s или vector-s, они автоматически управляют временем жизни ваших объектов.

Технически это проблема распределения памяти, однако здесь есть еще два практических аспекта этого. Он имеет отношение к двум вещам: 1) Область, когда вы определяете объект без указателя, вы больше не сможете получить к нему доступ после того, как блок кода он определен, тогда как если вы определяете указатель с «новым», тогда вы можно получить доступ к нему из любого места, где у вас есть указатель на эту память, пока вы не назовете «удалить» на том же указателе. 2) Если вы хотите передать аргументы функции, которую вы хотите передать указателю или ссылке, чтобы быть более эффективными. Когда вы передаете объект Object, объект копируется, если это объект, который использует много памяти, это может быть потребляемое ЦП (например, вы копируете вектор, полный данных). When you pass a pointer all you pass is one int (depending of implementation but most of them are one int).

Other than that you need to understand that “new” allocates memory on the heap that needs to be freed at some point. When you don’t have to use “new” I suggest you use a regular object definition “on the stack”.

Well the main question is Why should I use a pointer rather than the object itself? And my answer, you should (almost) never use pointer instead of object, because C++ has references , it is safer then pointers and guarantees the same performance as pointers.

Another thing you mentioned in your question:

 Object *myObject = new Object; 

How does it work? It creates pointer of Object type, allocates memory to fit one object and calls default constructor, sounds good, right? But actually it isn’t so good, if you dynamically allocated memory (used keyword new ), you also have to free memory manually, that means in code you should have:

 delete myObject; 

This calls destructor and frees memory, looks easy, however in big projects may be difficult to detect if one thread freed memory or not, but for that purpose you can try shared pointers , these slightly decreases performance, but it is much easier to work with them.


And now some introduction is over and go back to question.

You can use pointers instead of objects to get better performance while transferring data between function.

Take a look, you have std::string (it is also object) and it contains really much data, for example big XML, now you need to parse it, but for that you have function void foo(...) which can be declarated in different ways:

  1. void foo(std::string xml); In this case you will copy all data from your variable to function stack, it takes some time, so your performance will be low.
  2. void foo(std::string* xml); In this case you will pass pointer to object, same speed as passing size_t variable, however this declaration has error prone, because you can pass NULL pointer or invalid pointer. Pointers usually used in C because it doesn’t have references.
  3. void foo(std::string& xml); Here you pass reference, basically it is the same as passing pointer, but compiler does some stuff and you cannot pass invalid reference (actually it is possible to create situation with invalid reference, but it is tricking compiler).
  4. void foo(const std::string* xml); Here is the same as second, just pointer value cannot be changed.
  5. void foo(const std::string& xml); Here is the same as third, but object value cannot be changed.

What more I want to mention, you can use these 5 ways to pass data no matter which allocation way you have chosen (with new or regular ).


Another thing to mention, when you create object in regular way, you allocate memory in stack, but while you create it with new you allocate heap. It is much faster to allocate stack, but it is kind a small for really big arrays of data, so if you need big object you should use heap, because you may get stack overflow, but usually this issue is solved using STL containers and remember std::string is also container, some guys forgot it 🙂

Let’s say that you have class A that contain class B When you want to call some function of class B outside class A you will simply obtain a pointer to this class and you can do whatever you want and it will also change context of class B in your class A

But be careful with dynamic object

There are many benefits of using pointers to object –

  1. Efficiency (as you already pointed out). Passing objects to functions mean creating new copies of object.
  2. Working with objects from third party libraries. If your object belongs to a third party code and the authors intend the usage of their objects through pointers only (no copy constructors etc) the only way you can pass around this object is using pointers. Passing by value may cause issues. (Deep copy / shallow copy issues).
  3. if the object owns a resource and you want that the ownership should not be sahred with other objects.

This is has been discussed at length, but in Java everything is a pointer. It makes no distinction between stack and heap allocations (all objects are allocated on the heap), so you don’t realize you’re using pointers. In C++, you can mix the two, depending on your memory requirements. Performance and memory usage is more deterministic in C++ (duh).

 Object *myObject = new Object; 

Doing this will create a reference to an Object (on the heap) which has to be deleted explicitly to avoid memory leak .

 Object myObject; 

Doing this will create an object(myObject) of the automatic type (on the stack) that will be automatically deleted when the object(myObject) goes out of scope.

A pointer directly references the memory location of an object. Java has nothing like this. Java has references that reference the location of object through hash tables. You cannot do anything like pointer arithmetic in Java with these references.

To answer your question, it’s just your preference. I prefer using the Java-like syntax.

You shouldn’t . People (many people, sadly) write it out of ignorance.

Sometimes dynamic allocation has its place but, in the examples you give, it is wrong .

If you want to think about efficiency, then this is worse , because it introduces indirection for no good reason. This sort of programming is slower and more error-prone .

With pointers ,

  • can directly talk to the memory.

  • can prevent lot of memory leaks of a program by manipulating pointers.

One reason for using pointers is to interface with C functions. Another reason is to save memory; for example: instead of passing an object which contains a lot of data and has a processor-intensive copy-constructor to a function, just pass a pointer to the object, saving memory and speed especially if you’re in a loop, however a reference would be better in that case, unless you’re using an C-style array.

In areas where memory utilization is at its premium , pointers comes handy. For example consider a minimax algorithm, where thousands of nodes will be generated using recursive routine, and later use them to evaluate the next best move in game, ability to deallocate or reset (as in smart pointers) significantly reduces memory consumption. Whereas the non-pointer variable continues to occupy space till it’s recursive call returns a value.

I will include one important use case of pointer. When you are storing some object in the base class, but it could be polymorphic.

 Class Base1 { }; Class Derived1 : public Base1 { }; Class Base2 { Base *bObj; virtual void createMemerObects() = 0; }; Class Derived2 { virtual void createMemerObects() { bObj = new Derived1(); } }; 

So in this case you can’t declare bObj as an direct object, you have to have pointer.

“Necessity is the mother of invention.” The most of important difference that I would like to point out is the outcome of my own experience of coding. Sometimes you need to pass objects to functions. In that case, if your object is of a very big class then passing it as an object will copy its state (which you might not want ..AND CAN BE BIG OVERHEAD) thus resulting in an overhead of copying object .while pointer is fixed 4-byte size (assuming 32 bit). Other reasons are already mentioned above…

There are many excellent answers already, but let me give you one example:

I have an simple Item class:

  class Item { public: std::string name; int weight; int price; }; 

I make a vector to hold a bunch of them.

std::vector inventory;

I create one million Item objects, and push them back onto the vector. I sort the vector by name, and then do a simple iterative binary search for a particular item name. I test the program, and it takes over 8 minutes to finish executing. Then I change my inventory vector like so:

std::vector inventory;

…and create my million Item objects via new. The ONLY changes I make to my code are to use the pointers to Items, excepting a loop I add for memory cleanup at the end. That program runs in under 40 seconds, or better than a 10x speed increase. EDIT: The code is at http://pastebin.com/DK24SPeW With compiler optimizations it shows only a 3.4x increase on the machine I just tested it on, which is still considerable.

  • указатель на массив c ++
  • Почему я могу изменить локальную константную переменную с помощью указателей, но не глобальную в C?
  • Арифметика указателей: ++ * ptr или * ptr ++?
  • Исключение для массива, не затухающего в указатель?
  • Обоснование макроса container_of в linux / list.h
  • Вызывать функцию golang struct дает «не может ссылаться на невыполненное поле или метод»
  • Ошибка с адресом функции-члена в скобки
  • Разница между std :: reference_wrapper и простым указателем?
  • Является ли хорошей практикой NULL указатель после его удаления?
  • Почему polymorphism не работает без указателей / ссылок?
  • Как распечатать адрес памяти в C
  • Давайте будем гением компьютера.