Последствия использования только стека в С++

Позволяет сказать, что я знаю парня, который не знаком с С++. Он не проходит вокруг указателей (правильно), но он отказывается пройти по ссылке. Он всегда использует пропуск по значению. Причина в том, что он чувствует, что "передача объектов по ссылке является признаком сломанного дизайна".

Программа представляет собой небольшую графическую программу, и большая часть рассматриваемого вопроса является математическим вектором (3-кортежей). Есть несколько больших объектов контроллера, но ничего сложнее.

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

Я бы сказал, что пропуск по значению отлично подходит для небольших объектов, таких как векторы, но даже тогда в коде много ненужного копирования. Передача больших объектов по стоимости явно расточительна и, скорее всего, не то, что вы хотите функционально.

На стороне pro, я считаю, что стек быстрее выделяет/освобождает память и имеет постоянное время размещения.

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

Ответы

Ответ 1

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

Ответ 2

Есть всевозможные вещи, которые нельзя сделать без использования ссылок - начиная с конструктора копирования. Ссылки (или указатели) являются фундаментальными, и нравится ему это или нет, он использует ссылки. (Одно из преимуществ, или, может быть, недостаток ссылок, заключается в том, что вам вообще не нужно изменять код, чтобы передать ссылку (const).) И нет причин не использовать ссылки большую часть времени.

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

1 Различные атрибуты, в том числе C A Hoare (хотя, по-видимому, он отказывается от него).

Ответ 3

Ваша проблема с другом - это не его идея, как его религия. При любой функции всегда учитывайте плюсы и минусы передачи по значению, ссылке, константной ссылке, указателю или умному указателю. Затем решите.

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

Тем не менее, есть несколько подписей, которые не приносят большого значения в таблицу. Взятие const по значению может быть глупо, потому что, если вы обещаете не менять объект, вы также можете не сделать свою собственную копию. Разумеется, если это не примитив, то в этом случае компилятор может быть достаточно умным, чтобы взять ссылку. Или, иногда это неуклюже, чтобы указать указатель на указатель в качестве аргумента. Это добавляет сложности; вместо этого вы могли бы уйти от него, взяв ссылку на указатель и получив тот же эффект.

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

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

Ответ 4

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

Хотя это не так в С++ по чисто техническим причинам, всегда использовать pass-by-value - достаточно хорошее приближение для новичков - это, безусловно, намного лучше, чем передавать все указатели (или, возможно, даже передавать все по ссылке). Это сделает некоторый код неэффективным, но, эй! Пока это не беспокоит вашего друга, не следует чрезмерно беспокоить эту практику. Просто напомните ему, что когда-нибудь он захочет пересмотреть.

С другой стороны, это:

Есть несколько больших объектов контроллера, но ничего сложнее.

- проблема. Ваш друг говорит о сломанном дизайне, а затем весь код использует несколько 3D-векторов и больших структур управления? Это сломанный дизайн. Хороший код обеспечивает модульность благодаря использованию структур данных. Кажется, это не так.

... И как только вы используете такие структуры данных, код без сквозной ссылки действительно может стать довольно неэффективным.

Ответ 5

Я думаю, что в самом вопросе есть огромное недоразумение.

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

Распределение стека и кучи

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

Это может быть невозможно в нескольких ситуациях:

  • Виртуальное построение (подумайте о Factory)
  • Общая собственность (хотя вы всегда должны ее избегать)

И я мог бы пропустить некоторые из них, но в этом случае вы должны использовать SBRM (Scope Bound Resources Management), чтобы использовать возможности управления жизненным циклом стека, например, используя интеллектуальные указатели.

Pass by: значение, ссылка, указатель

Прежде всего, существует разница семантики:

  • value, const reference: переданный объект не будет изменен методом
  • ссылка: переданный объект может быть изменен с помощью метода
  • указатель указателя /const: то же, что и ссылка (для поведения), но может быть null

Обратите внимание, что некоторые языки (такие функциональные, как Haskell) по умолчанию не предоставляют ссылку/указатель. Значения неизменяемы после создания. Помимо некоторых проблем, связанных с внешней средой, они не ограничиваются этим использованием, и это как-то облегчает отладку.

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

Наконец, полиморфизм не позволяет использовать семантику pass-by-value.

Теперь расскажите о выступлениях.

Обычно принято считать, что встроенные модули должны передаваться по значению (чтобы избежать косвенности), а определяемые пользователем большие классы должны передаваться по ссылке/указателю (чтобы избежать копирования). на самом деле, обычно означает, что Copy Constructor не является тривиальным.

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

Object foo(Object d) { d.bar(); return d; }

int main(int argc, char* argv[])
{
  Object o;
  o = foo(o);
  return 0;
}

Здесь интеллектуальный компилятор может определить, что o может быть изменен без каких-либо копий! (Необходимо, чтобы определение функции было видимым, я думаю, я не знаю, сможет ли Оптимизация Link-Time разобраться)

Следовательно, есть только одна возможность проблемы производительности, как всегда: measure.

Ответ 6

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

Похоже, ваш друг боится указателей.

Помните, что указатели С++ были фактически унаследованы от языка C, а C был разработан, когда компьютеры были намного менее мощными. Тем не менее, скорость и эффективность по-прежнему остаются жизненно важными до сих пор.

Итак, зачем использовать указатели?. Они позволяют разработчику оптимизировать работу программы быстрее или использовать меньше памяти, чем в противном случае! Ссылаясь на расположение памяти данных, намного эффективнее, чем копирование всех данных.

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

Так что немного преувеличивайте. Подумайте о массиве char с 2147483648 элементами - 2 ГБ данных - вам нужно перейти к функции, которая будет записывать все данные на диск. Теперь, какая техника, по вашему мнению, будет более эффективной/быстрой?

  • Перейдите по значению, которое должно будет повторно скопировать эти 2 ГБ данных в другое место в памяти, прежде чем программа сможет записать данные на диск или
  • Перейдите по ссылке, которая будет просто ссылаться на эту ячейку памяти.

Что произойдет, если у вас просто нет 4 ГБ ОЗУ? Вы будете тратить $и покупать чипы ОЗУ только потому, что боитесь использовать указатели?

Повторное копирование данных в памяти звучит немного избыточно, когда вам этого не нужно, и его отходы от компьютерных ресурсов.

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

Удачи.

Ответ 7

Прежде всего, стек редко переполняется за пределами этого веб-сайта, за исключением случая рекурсии.

О своих рассуждениях я думаю, что он может ошибаться, потому что он слишком обобщен, но то, что он сделал, может быть правильным... или нет?

Например, в библиотеке Windows Forms используется структура Rectangle, состоящая из 4 членов, у Apple QuartzCore также есть CGRect struct, и эти структуры всегда передаются по значению. Я думаю, мы можем сравнить это с Vector с 3-мя переменными с плавающей запятой.

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

Ответ 8

Я бы сказал, что пропуск по значению отлично подходит для небольших объектов, таких как векторы, но даже тогда в коде много ненужного копирования. Передача больших объектов по стоимости явно расточительна и, скорее всего, не то, что вы хотите функционально.

Это не так очевидно, как вы думаете. Компиляторы С++ выполняют операцию копирования очень агрессивно, поэтому вы часто можете передавать значение без затрат на операцию копирования. И в некоторых случаях передача значения может быть даже быстрее.

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

Итак, настоящая проблема должна быть одной из семантики. Как вы хотите, чтобы ваш код работал? Иногда ссылочная семантика - это то, что вы хотите, а затем вы должны пройти по ссылке. Если вы специально хотите/нуждаетесь в семантике значений, вы передаете значение.

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

Но на самом деле оба имеют свое место. И никогда не использовать pass-by-reference, безусловно, большой предупреждающий знак.

В течение последних 6 месяцев или около того я экспериментировал с принятием по умолчанию значения по умолчанию. Если мне явно не нужна эталонная семантика, я пытаюсь предположить, что компилятор выполнит для меня копию, поэтому я могу пройти по значению, не теряя при этом никакой эффективности.

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

  • производительность - это проблема, и
  • компилятор не смог применить копию elision

Ответ 9

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

Указанный недостаток "недостаток" просто не соответствует действительности. Передача всего по стоимости полностью изменит производительность приложения. Это не так плохо, когда примитивные типы (т.е. Int, double и т.д.) Передаются по значению, но когда экземпляр класса передается по значению, создаются временные объекты, которые требуют, чтобы конструкторы, а затем деструктор вызывались в классе и на всех переменной-члена в классе. Это раздражает, когда используются иерархии больших классов, потому что также должны быть вызваны конструкторы родительских классов/деструктор.

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

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

Ответ 10

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

Маленькие кортежи или векторы представляют собой очень простой тип структуры данных. Более сложные структуры данных обмениваются информацией, и что совместное использование не может быть представлено непосредственно в качестве значений. Вам либо нужно использовать ссылки/указатели или что-то, что имитирует их, например массивы и индексы.

Множество проблем сводится к данным, которые образуют график или Directed-Graph. В обоих случаях у вас есть смесь ребер и узлов, которые необходимо сохранить в структуре данных. Теперь у вас есть проблема, что одни и те же данные должны быть в нескольких местах. Если вы избегаете ссылок, сначала необходимо дублировать данные, а затем каждое изменение должно быть тщательно реплицировано в каждой из других копий.

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

Ответ 11

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

Ну, черт возьми, с чего начать...

  • Как вы говорите, "в коде много ненужного копирования". Скажем, у вас есть цикл, в котором вы вызываете функцию на этих объектах. Использование указателя вместо дублирования объектов может ускорить выполнение на один или несколько порядков.

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

  • Как вы помните, рассматриваемая программа проста и в основном использует довольно маленькие объекты, такие как графические 3-кортежи, которые, если элементы удваиваются, будут составлять 24 байта за штуку. Но в графике он имеет дело с массивами 4x4, которые обрабатывают как поворот, так и перевод. Это будет 128 байт за штуку, поэтому, если программа, которая должна была иметь дело с ними, будет в пять раз медленнее на вызов функции с передачей по значению из-за увеличения копирования. С помощью pass-by-reference передача 3-кортежа или массива 4x4 в 32-битном исполняемом файле будет включать в себя дублирование одного 4-байтового указателя.

  • В архивных архивах с поддержкой регистров, таких как ARM, PowerPC, 64-разрядные x86, 680x0, но не 32-разрядные x86-указатели (и ссылки, которые являются тайными указателями, носящими необычную синтаксическую одежду), обычно передаются или возвращенный в регистр, который действительно freaking быстро по сравнению с доступом к памяти, участвующим в операции стека.

  • Вы говорите о невероятности окончания пространства стека. И да, это так на маленькой программе, которую можно написать для присвоения класса. Но пару месяцев назад я отлаживал коммерческий код, который был, вероятно, 80 вызовами функций ниже main(). Если бы они использовали pass-by-value вместо pass-by-reference, стек был бы ginormous. И чтобы ваш друг не подумал, что это был "сломанный дизайн", на самом деле это был браузер на основе WebKit, реализованный в Linux с использованием GTK +, все из которых очень современны, а глубина вызова функции является нормальной для профессионального кода.

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

Я мог бы продолжать и продолжать.

Если ваш друг интересуется графикой, он должен взглянуть на некоторые из распространенных API, используемых в графике: OpenGL и XWindows на Linux, Quartz на Mac OS X, Direct X на Windows. И он должен посмотреть на внутренности больших систем C/С++, таких как движки HTML или HTML, или любой из браузеров Mozilla или GTK + или Qt GUI. Все они проходят через нечто большее, чем одно целое число или плавают по ссылке, и часто заполняют результаты по ссылке, а не как возвращаемое значение функции.

Никто из серьезных серьезов C/С++ в реальном мире - и я имею в виду nobody - передает структуры данных по значению. Там есть причина для этого: он просто переворачивает неэффективные и проблемные.

Ответ 12

Ничего себе, уже есть 13 ответов... Я не читал все подробно, но я думаю, что это сильно отличается от других...

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

Просто объясните ему разницу между vector3 & и vector3 const& и продемонстрируйте, как последняя может быть инициализирована константой, как в vec_function( vector3(1,2,3) );, но не первой. Pass by const reference - простая оптимизация прохода по значению.

Ответ 13

Купите своему другу хорошую книгу на С++. Передача нетривиальных объектов по ссылке является хорошей практикой и сэкономит вам много ненужных вызовов конструктора/деструктора. Это также не имеет никакого отношения к распределению на free store vs. using stack. Вы можете (или должны) передавать объекты, выделенные в стеке программ, посредством ссылки без использования свободного хранилища. Вы также можете полностью игнорировать бесплатный магазин, но это возвращает вас к старым fortran дням, которые ваш друг, вероятно, не имел в виду, - иначе он выберет древний компилятор f77 для вашего проекта, не так ли?