Я пришел из Java-фона и начал работать с объектами в C ++. Но одна вещь, которая пришла мне в голову, это то, что люди часто используют указатели на объекты, а не на сами объекты, например, это объявление:
Object *myObject = new Object;
скорее, чем:
Object myObject;
Или вместо использования функции, скажем testFunc()
так:
myObject.testFunc();
мы должны написать:
myObject->testFunc();
Но я не могу понять, почему мы должны делать это таким образом. Я предполагаю, что это связано с эффективностью и скоростью, поскольку мы получаем прямой доступ к адресу памяти. Я прав?
Ответы:
Очень жаль, что вы так часто видите динамическое распределение. Это просто показывает, сколько есть плохих программистов на C ++.
В некотором смысле, у вас есть два вопроса, объединенные в один. Во-первых, когда мы должны использовать динамическое распределение (использование
new
)? Во-вторых, когда мы должны использовать указатели?Важное сообщение о том, что вы всегда должны использовать соответствующий инструмент для работы . Почти во всех ситуациях есть что-то более подходящее и более безопасное, чем выполнение динамического выделения вручную и / или использование необработанных указателей.
Динамическое распределение
В своем вопросе вы продемонстрировали два способа создания объекта. Основным отличием является срок хранения объекта. При выполнении
Object myObject;
внутри блока объект создается с автоматическим сроком хранения, что означает, что он будет автоматически уничтожен, когда выйдет из области видимости. Когда вы это делаетеnew Object()
, объект имеет динамическую длительность хранения, что означает, что он остается в живых, пока вы явноdelete
не сделаете это. Вы должны использовать динамическую продолжительность хранения только тогда, когда вам это нужно. То есть вы должны всегда предпочитать создавать объекты с автоматической продолжительностью хранения, когда можете .Основные две ситуации, в которых вам может потребоваться динамическое размещение:
Когда вам абсолютно необходимо динамическое распределение, вы должны инкапсулировать его в умный указатель или какой-то другой тип, который выполняет RAII (например, стандартные контейнеры). Умные указатели обеспечивают семантику владения динамически размещенными объектами. Взгляните на
std::unique_ptr
иstd::shared_ptr
, например. Если вы используете их надлежащим образом, вы можете почти полностью избежать выполнения своего собственного управления памятью (см. Правило нуля ).указатели
Тем не менее, есть и другие, более общие способы использования необработанных указателей, помимо динамического выделения, но у большинства есть альтернативы, которые вы должны предпочесть. Как и прежде, всегда предпочитайте альтернативы, если вам не нужны указатели .
Вам нужна ссылочная семантика . Иногда вы хотите передать объект с помощью указателя (независимо от того, как он был выделен), потому что вы хотите, чтобы функция, которой вы его передаете, имела доступ к этому конкретному объекту (а не к его копии). Однако в большинстве ситуаций вы должны предпочитать ссылочные типы указателям, потому что это именно то, для чего они предназначены. Обратите внимание, что это не обязательно означает продление срока службы объекта за пределы текущей области, как в ситуации 1 выше. Как и раньше, если вы в порядке с передачей копии объекта, вам не нужна ссылочная семантика.
Вам нужен полиморфизм . Вы можете вызывать функции только полиморфно (то есть в соответствии с динамическим типом объекта) через указатель или ссылку на объект. Если вам нужно именно такое поведение, вам нужно использовать указатели или ссылки. Опять же, ссылки должны быть предпочтительными.
Вы хотите представить, что объект является необязательным , позволяя
nullptr
передавать его, когда объект опущен. Если это аргумент, вы должны использовать аргументы по умолчанию или перегрузки функций. В противном случае вам лучше использовать тип, который инкапсулирует это поведение, напримерstd::optional
(используйте в C ++ 17 - с более ранними стандартами C ++, используйтеboost::optional
).Вы хотите отделить единицы компиляции, чтобы улучшить время компиляции . Полезное свойство указателя заключается в том, что вам требуется только предварительное объявление указательного типа (для фактического использования объекта вам понадобится определение). Это позволяет вам отделить части вашего процесса компиляции, что может значительно сократить время компиляции. Смотрите идиома Пимпл .
Вам необходимо взаимодействовать с библиотекой C или библиотекой в стиле C. На этом этапе вы вынуждены использовать сырые указатели. Лучшее, что вы можете сделать, это убедиться, что ваши сырые указатели теряются только в самый последний момент. Вы можете получить необработанный указатель из умного указателя, например, используя его
get
функцию-член. Если библиотека выполняет для вас какое-то выделение, которое она ожидает от вас освободить с помощью дескриптора, вы часто можете заключить этот дескриптор в интеллектуальный указатель с помощью специального средства удаления, которое будет соответствующим образом освобождать объект.источник
Object myObject(param1, etc...)
Есть много вариантов использования указателей.
Полиморфное поведение . Для полиморфных типов указатели (или ссылки) используются, чтобы избежать нарезки:
Ссылочная семантика и избегание копирования . Для неполиморфных типов указатель (или ссылка) позволит избежать копирования потенциально дорогого объекта
Обратите внимание, что C ++ 11 имеет семантику перемещения, которая позволяет избежать многих копий дорогих объектов в аргумент функции и в качестве возвращаемых значений. Но использование указателя определенно позволит избежать этого и позволит использовать несколько указателей на один и тот же объект (тогда как объект можно перемещать только один раз).
Приобретение ресурса . Создание указателя на ресурс с помощью
new
оператора является антишаблоном в современном C ++. Используйте специальный класс ресурсов (один из стандартных контейнеров) или умный указатель (std::unique_ptr<>
илиstd::shared_ptr<>
). Рассматривать:против
Необработанный указатель должен использоваться только как «представление» и никоим образом не связан с владением, будь то путем прямого создания или неявным образом через возвращаемые значения. Смотрите также эти вопросы и ответы из C ++ FAQ .
Более детальный контроль времени жизни Каждый раз, когда разделяемый указатель копируется (например, в качестве аргумента функции), ресурс, на который он указывает, сохраняется живым. Обычные объекты (не созданные
new
непосредственно вами или внутри класса ресурсов) уничтожаются при выходе из области видимости.источник
unique_ptr
семантика / movehun(b)
также требует знания подписи, если вы не уверены, что не указали неправильный тип до компиляции. Хотя проблема со ссылками обычно не обнаруживается во время компиляции и требует больше усилий для отладки, если вы проверяете подпись, чтобы убедиться в правильности аргументов, вы также сможете увидеть, являются ли какие-либо из аргументов ссылочными. таким образом, бит ссылки становится чем-то без проблем (особенно при использовании IDE или текстовых редакторов, которые показывают сигнатуру выбранных функций). Такжеconst&
.Есть много отличных ответов на этот вопрос, включая важные случаи использования предварительных объявлений, полиморфизма и т. Д., Но я чувствую, что часть «души» вашего вопроса не получила ответа, а именно, что означают различные синтаксисы в Java и C ++.
Давайте рассмотрим ситуацию, сравнивая два языка:
Ява:
Ближайший эквивалент этому:
C ++:
Давайте посмотрим альтернативный путь C ++:
Лучший способ думать об этом - то, что - более или менее - Java (неявно) обрабатывает указатели на объекты, в то время как C ++ может обрабатывать либо указатели на объекты, либо сами объекты. Есть исключения из этого - например, если вы объявляете «примитивные» типы Java, они являются фактическими копируемыми значениями, а не указателями. Так,
Ява:
Тем не менее, использование указателей не обязательно является правильным или неправильным способом обработки вещей; однако другие ответы удовлетворительно это охватили. Тем не менее, общая идея заключается в том, что в C ++ у вас гораздо больше контроля над временем жизни объектов и тем, где они будут жить.
Вернемся к сути - эта
Object * object = new Object()
конструкция на самом деле является самой близкой к типичной семантике Java (или C #).источник
Object2 is now "dead"
Я думаю, что вы имеете в видуmyObject1
или точнееthe object pointed to by myObject1
.Object object1 = new Object(); Object object2 = new Object();
очень плохой код Второй новый или второй конструктор Object может выдать, и теперь object1 просочился. Если вы используете rawnew
s, вы должны обернутьnew
объекты ed в оболочки RAII как можно скорее.Еще одна веская причина использовать указатели для предварительных объявлений . В достаточно большом проекте они действительно могут ускорить время компиляции.
источник
std::unique_ptr<T>
работает с предварительными декларациямиT
. Вам просто нужно убедиться, что когдаstd::unique_ptr<T>
вызывается деструктор ,T
это полный тип. Обычно это означает, что ваш класс, содержащий объявление,std::unique_ptr<T>
объявляет свой деструктор в заголовочном файле и реализует его в файле cpp (даже если реализация пуста).Предисловие
Java не похож на C ++, в отличие от рекламы. Машина для раскрутки Java хотела бы, чтобы вы поверили, что, поскольку Java имеет синтаксис, подобный C ++, языки схожи. Ничто не может быть дальше от правды. Эта дезинформация является частью причины, по которой Java-программисты переходят на C ++ и используют Java-подобный синтаксис, не понимая значения своего кода.
Вперед мы идем
На самом деле наоборот. Куча намного медленнее стека, потому что стек очень прост по сравнению с кучей. Переменные автоматического хранения (или переменные стека) имеют свои деструкторы, вызываемые после выхода из области видимости. Например:
С другой стороны, если вы используете динамически размещаемый указатель, его деструктор должен вызываться вручную.
delete
называет это деструктором для вас.Это не имеет ничего общего с
new
синтаксисом, распространенным в C # и Java. Они используются для совершенно разных целей.Преимущества динамического распределения
Одна из первых проблем, с которыми сталкиваются многие программисты на С ++, заключается в том, что когда они принимают произвольные данные от пользователей, вы можете выделить фиксированный размер только для переменной стека. Вы также не можете изменить размер массивов. Например:
Конечно, если вы использовали
std::string
вместо этого,std::string
внутренне изменяет размер себя, так что это не должно быть проблемой. Но, по сути, решением этой проблемы является динамическое распределение. Вы можете выделить динамическую память на основе ввода пользователя, например:Поскольку куча намного больше стека, можно произвольно выделить / перераспределить столько памяти, сколько ему нужно, в то время как у стека есть ограничение.
Какую пользу вы спрашиваете? Ответ станет ясен, как только вы поймете путаницу / миф о массивах и указателях. Обычно считается, что они одинаковы, но это не так. Этот миф проистекает из того факта, что указатели могут быть подписаны так же, как массивы, и из-за того, что массивы распадаются на указатели на верхнем уровне в объявлении функции. Однако, когда массив распадается на указатель, указатель теряет свою
sizeof
информацию. Таким образом,sizeof(pointer)
будет указан размер указателя в байтах, который обычно составляет 8 байтов в 64-битной системе.Вы не можете назначать массивы, только инициализировать их. Например:
С другой стороны, вы можете делать что угодно с указателями. К сожалению, поскольку в Java и C # различие между указателями и массивами выполняется вручную, новички не понимают разницы.
Java и C # имеют средства, которые позволяют вам рассматривать объекты как другие, например, используя
as
ключевое слово. Поэтому, если кто-то хочет рассматриватьEntity
объект какPlayer
объект, это можно сделать.Player player = Entity as Player;
Это очень полезно, если вы собираетесь вызывать функции в однородном контейнере, который должен применяться только к определенному типу. Функциональность может быть достигнута аналогичным образом ниже:Так, скажем, если бы только у треугольников была функция Rotate, это было бы ошибкой компилятора, если бы вы попытались вызвать ее для всех объектов класса. Используя
dynamic_cast
, вы можете моделироватьas
ключевое слово. Для ясности, если приведение не выполнено, возвращается неверный указатель. Таким образом,!test
это, по сути, сокращение для проверки,test
является ли NULL или недопустимый указатель, что означает, что приведение не удалось.Преимущества автоматических переменных
После просмотра всех замечательных вещей, которые может сделать динамическое распределение, вы, вероятно, задаетесь вопросом, почему бы никому НЕ использовать динамическое распределение все время? Я уже говорил вам одну причину, куча медленная. И если вам не нужна вся эта память, вы не должны злоупотреблять ею. Итак, вот некоторые недостатки в произвольном порядке:
Это подвержено ошибкам. Ручное распределение памяти опасно, и вы склонны к утечкам. Если вы не разбираетесь в использовании отладчика или
valgrind
(средством утечки памяти), вы можете вырвать волосы из головы. К счастью, идиомы RAII и умные указатели немного смягчают это, но вы должны быть знакомы с такими практиками, как Правило Трех и Правило Пяти. Здесь много информации, и новички, которые не знают или не заботятся, попадут в эту ловушку.Это не обязательно. В отличие от Java и C #, где использование
new
ключевого слова везде идиоматично , в C ++ его следует использовать только при необходимости. Общая фраза гласит: все выглядит как гвоздь, если у вас есть молоток. В то время как новички, начинающие с C ++, боятся указателей и учатся использовать переменные стека по привычке, программисты на Java и C # начинают с использования указателей, не понимая этого! Это буквально ступает не на ту ногу. Вы должны отказаться от всего, что вы знаете, потому что синтаксис - это одно, а изучение языка - это другое.Одна оптимизация, которую делают многие компиляторы, это то, что называется оптимизацией elision и return value . Эти вещи могут устранить ненужные копии, которые полезны для очень больших объектов, таких как вектор, содержащий много элементов. Обычно обычной практикой является использование указателей для передачи прав собственности, а не копирование больших объектов для их перемещения . Это привело к появлению семантики перемещения и умных указателей. .
Если вы используете указатели, (N) RVO НЕ происходит. Более выгодно и менее подвержено ошибкам использовать преимущество (N) RVO, а не возвращать или передавать указатели, если вы беспокоитесь об оптимизации. Утечка ошибок может произойти, если вызывающая функция ответственна за
delete
динамически размещенный объект и тому подобное. Может быть трудно отследить право собственности на объект, если указатели передаются как горячий картофель. Просто используйте переменные стека, потому что это проще и лучше.источник
{ std::string* s = new std::string; } delete s; // destructor called
.... конечно, этоdelete
не будет работать, потому что компилятор не будет знать, чтоs
больше?C ++ дает вам три способа передачи объекта: по указателю, по ссылке и по значению. Java ограничивает вас последним (единственным исключением являются примитивные типы, такие как int, boolean и т. Д.). Если вы хотите использовать C ++ не просто как странную игрушку, вам лучше узнать разницу между этими тремя способами.
Java делает вид, что нет такой проблемы, как «кто и когда должен это уничтожить?». Ответ: Сборщик мусора, Великий и Ужасный. Тем не менее, он не может обеспечить 100% защиту от утечек памяти (да, Java может утечка памяти ). На самом деле, GC дает вам ложное чувство безопасности. Чем больше ваш внедорожник, тем дольше вы добираетесь до эвакуатора.
C ++ оставляет вас лицом к лицу с управлением жизненным циклом объекта. Что ж, есть способы справиться с этим ( семейство умных указателей , QObject в Qt и т. Д.), Но ни один из них не может быть использован способом «забыл и забыл», как GC: вы всегда должны помнить об обработке памяти. Вы должны заботиться не только об уничтожении объекта, но и об уничтожении одного и того же объекта более одного раза.
Еще не напуган? ОК: циклические ссылки - обращайтесь с ними сами, человек. И помните: убивайте каждый объект ровно один раз, нам, во время выполнения C ++, не нравятся те, кто возится с трупами, оставляя мертвых в покое.
Итак, вернемся к вашему вопросу.
Когда вы передаете свой объект по значению, а не по указателю или по ссылке, вы копируете объект (весь объект, будь то пара байтов или огромный дамп базы данных) - вы достаточно умен, чтобы избежать последнего, не так ли? не так ли?) каждый раз, когда вы делаете '='. И для доступа к членам объекта вы используете '.' (Точка).
Когда вы передаете ваш объект по указателю, вы копируете всего несколько байтов (4 в 32-битных системах, 8 в 64-битных), а именно - адрес этого объекта. И чтобы показать это всем, вы используете этот причудливый оператор «->» при доступе к участникам. Или вы можете использовать комбинацию «*» и «.».
Когда вы используете ссылки, вы получаете указатель, который претендует на значение. Это указатель, но вы получаете доступ к членам через «.».
И, чтобы поразить вас еще раз: когда вы объявляете несколько переменных, разделенных запятыми, тогда (следите за руками):
Пример:
источник
std::auto_ptr
устарела, пожалуйста, не используйте его.В C ++ объекты, размещенные в стеке (используя
Object object;
оператор внутри блока), будут жить только в той области, в которой они объявлены. Когда блок кода завершает выполнение, объявленный объект уничтожается. Принимая во внимание, что если вы выделяете память в куче, используяObject* obj = new Object()
, они продолжают жить в куче, пока вы не позвонитеdelete obj
.Я хотел бы создать объект в куче, когда мне нравится использовать объект не только в блоке кода, который объявил / разместил его.
источник
Object obj
не всегда в стеке - например, глобальные переменные или переменные-члены.Я сравню, как это работает внутри тела функции, если вы используете:
Внутри функции ваш
myObject
будет уничтожен, как только эта функция вернется. Так что это полезно, если вам не нужен ваш объект вне вашей функции. Этот объект будет помещен в стек текущего потока.Если вы напишите внутри тела функции:
тогда экземпляр класса Object, на который указывает объект
myObject
, не будет уничтожен после завершения функции и выделения в куче.Если вы программист на Java, то второй пример ближе к тому, как работает распределение объектов в Java. Эта строка:
Object *myObject = new Object;
эквивалентно Java:Object myObject = new Object();
. Разница в том, что под java myObject будет собирать мусор, а под c ++ он не будет освобожден, вы должны где-то явно вызывать `delete myObject; ' в противном случае вы будете вводить утечки памяти.Начиная с c ++ 11 вы можете использовать безопасные способы динамического распределения:
new Object
путем хранения значений в shared_ptr / unique_ptr.Кроме того, объекты очень часто хранятся в контейнерах, таких как map-s или vector-s, они автоматически управляют временем жизни ваших объектов.
источник
then myObject will not get destroyed once function ends
Это абсолютно будет.myObject
все равно будет уничтожено, как и любая другая локальная переменная. Разница в том, что его значение является указателем на объект, а не на сам объект, и уничтожение тупого указателя не влияет на его объект. Так что объект переживет указанное разрушение.Технически это проблема выделения памяти, однако здесь есть еще два практических аспекта этого. Это связано с двумя вещами: 1) Область действия: когда вы определяете объект без указателя, вы больше не сможете получить к нему доступ после того блока кода, в котором он определен, тогда как если вы определите указатель с помощью «new», то вы можете получить к нему доступ из любого места, где у вас есть указатель на эту память, пока вы не вызовете «delete» для того же указателя. 2) Если вы хотите передать аргументы функции, вы хотите передать указатель или ссылку, чтобы быть более эффективным. Когда вы передаете Object, тогда объект копируется, если это объект, который использует много памяти, это может потреблять процессор (например, вы копируете вектор, полный данных). Когда вы передаете указатель, вы просто передаете один int (в зависимости от реализации, но большинство из них - один int).
Помимо этого вам нужно понимать, что «new» выделяет память в куче, которую нужно освободить в какой-то момент. Когда вам не нужно использовать «новый», я предлагаю вам использовать обычное определение объекта «в стеке».
источник
Ну, главный вопрос: почему я должен использовать указатель, а не сам объект? И мой ответ, вы (почти) никогда не должны использовать указатель вместо объекта, потому что C ++ имеет ссылки , он безопаснее указателей и гарантирует ту же производительность, что и указатели.
Еще одна вещь, которую вы упомянули в своем вопросе:
Как это работает? Он создает указатель
Object
типа, выделяет память под один объект и вызывает конструктор по умолчанию, хорошо звучит, верно? Но на самом деле это не так хорошо, если вы динамически распределяете память (используемое ключевое словоnew
), вам также необходимо освободить память вручную, это означает, что в коде вы должны иметь:Это вызывает деструктор и освобождает память, выглядит легко, однако в больших проектах может быть трудно определить, освободил ли один поток память или нет, но для этой цели вы можете использовать общие указатели , это немного снижает производительность, но работать с ними намного проще их.
И теперь некоторое введение закончено и вернемся к вопросу.
Вы можете использовать указатели вместо объектов для повышения производительности при передаче данных между функциями.
Посмотрите, у вас есть
std::string
(это также объект), и он содержит действительно много данных, например, большой XML, теперь вам нужно его проанализировать, но для этого у вас есть функция,void foo(...)
которая может быть объявлена различными способами:void foo(std::string xml);
В этом случае вы скопируете все данные из вашей переменной в стек функций, это займет некоторое время, поэтому ваша производительность будет низкой.void foo(std::string* xml);
В этом случае вы передадите указатель на объект с той же скоростью, что и передаваемаяsize_t
переменная, однако это объявление подвержено ошибкам, потому что вы можете передатьNULL
указатель или неверный указатель. Указатели обычно используются,C
потому что у них нет ссылок.void foo(std::string& xml);
Здесь вы передаете ссылку, в основном это то же самое, что и передающий указатель, но компилятор делает некоторые вещи, и вы не можете передать недопустимую ссылку (на самом деле можно создать ситуацию с недопустимой ссылкой, но это обманывает компилятор).void foo(const std::string* xml);
Здесь то же самое, что и второе, просто значение указателя не может быть изменено.void foo(const std::string& xml);
Здесь то же самое, что и третье, но значение объекта не может быть изменено.Более того, я хочу упомянуть, что вы можете использовать эти 5 способов передачи данных независимо от того, какой способ распределения вы выбрали (с помощью обычного
new
или обычного ).Еще одна вещь, о которой стоит упомянуть, когда вы создаете объект обычным способом, вы выделяете память в стеке, но пока вы создаете его вместе,
new
вы выделяете кучу. Выделение стека происходит намного быстрее, но это очень мало для действительно больших массивов данных, поэтому, если вам нужен большой объект, вы должны использовать кучу, потому что вы можете получить переполнение стека, но обычно эта проблема решается с помощью контейнеров STL и запомнитеstd::string
это тоже контейнер, некоторые ребята его забыли :)источник
Допустим, у вас есть
class A
это содержимое.class B
Когда вы захотите вызвать некоторую функциюclass B
извне,class A
вы просто получите указатель на этот класс, и вы можете делать все, что захотите, и это также изменит контекстclass B
вашегоclass A
Но будьте осторожны с динамическим объектом
источник
Есть много преимуществ использования указателей на объект -
источник
Это было обсуждено подробно, но в Java все является указателем. Он не делает различий между распределением стека и кучи (все объекты размещаются в куче), поэтому вы не понимаете, что используете указатели. В C ++ вы можете смешивать их в зависимости от ваших требований к памяти. Производительность и использование памяти более детерминированы в C ++ (дух).
источник
Это создаст ссылку на объект (в куче), который необходимо явно удалить, чтобы избежать утечки памяти .
Это создаст объект (myObject) автоматического типа (в стеке), который будет автоматически удален, когда объект (myObject) выйдет из области видимости.
источник
Указатель напрямую ссылается на местоположение объекта в памяти. Ява не имеет ничего подобного. У Java есть ссылки, которые ссылаются на местоположение объекта через хеш-таблицы. Вы не можете делать что-то вроде арифметики указателей в Java с этими ссылками.
Чтобы ответить на ваш вопрос, это просто ваши предпочтения. Я предпочитаю использовать Java-подобный синтаксис.
источник
С указателями ,
можно напрямую общаться с памятью.
может предотвратить много утечек памяти программы, манипулируя указателями.
источник
Одной из причин использования указателей является взаимодействие с функциями Си. Другая причина - сохранить память; Например: вместо того, чтобы передавать объект, который содержит много данных и имеет процессорно-интенсивный конструктор копирования, просто передайте указатель на объект, экономя память и скорость, особенно если вы находитесь в цикле, однако ссылка будет лучше в этом случае, если вы не используете массив в стиле C.
источник
В областях, где использование памяти находится на высшем уровне, указатели оказываются полезными. Например, рассмотрим минимаксный алгоритм, в котором тысячи узлов будут генерироваться с использованием рекурсивной процедуры, а затем использовать их для оценки следующего лучшего хода в игре. Возможность освобождения или сброса (как в интеллектуальных указателях) значительно снижает потребление памяти. В то время как переменная без указателя продолжает занимать пространство, пока ее рекурсивный вызов не вернет значение.
источник
Я включу один важный пример использования указателя. Когда вы храните некоторый объект в базовом классе, но это может быть полиморфным.
Так что в этом случае вы не можете объявить bObj как прямой объект, у вас должен быть указатель.
источник
"Голь на выдумки хитра." Самым важным отличием, на которое я хотел бы обратить внимание, является результат моего собственного опыта программирования. Иногда вам нужно передать объекты в функции. В этом случае, если ваш объект имеет очень большой класс, то передача его в качестве объекта скопирует его состояние (которое вы, возможно, не захотите .. И МОЖЕТ БЫТЬ БОЛЬШИМ ЗАГЛАВНЫМ), что приведет к накладным расходам на копирование объекта. Пока указатель исправлен 4-байтовый размер (при условии 32-битного). Другие причины уже упомянуты выше ...
источник
std::string test;
у нас переменной ,void func(const std::string &) {}
но если функции не нужно изменять ввод, в этом случае я рекомендую использовать указатели (чтобы любой, кто читает код, заметил&
и понял, что функция может изменить свой ввод)Уже есть много отличных ответов, но позвольте мне привести один пример:
У меня есть простой класс предметов:
Я делаю вектор, чтобы держать их несколько.
std::vector<Item> inventory;
Я создаю миллион объектов Item и помещаю их обратно в вектор. Я сортирую вектор по имени, а затем выполняю простой итеративный двоичный поиск определенного имени элемента. Я тестирую программу, и выполнение ее занимает более 8 минут. Затем я изменяю свой инвентарный вектор следующим образом:
std::vector<Item *> inventory;
... и создать мой миллион объектов Item с помощью нового. Единственные изменения, которые я делаю в своем коде, это использование указателей на Items, за исключением цикла, который я добавляю для очистки памяти в конце. Эта программа выполняется менее чем за 40 секунд, или лучше, чем увеличение скорости в 10 раз. РЕДАКТИРОВАТЬ: код находится на http://pastebin.com/DK24SPeW С оптимизацией компилятора он показывает только 3,4-кратное увеличение на машине, на которой я только что проверил, что все еще значительно.
источник
push_back
. Конечно это копии. Вы должны были бытьemplace
на месте при создании ваших объектов (если вам не нужно, чтобы они были кэшированы в другом месте).