Мне интересно, какие возможные достоинства есть у копирования при записи? Естественно, я не жду личных мнений, но реальных практических сценариев, где это может быть технически и практически выгодно ощутимым образом. И под осязаемым я имею в виду нечто большее, чем спасение вас от набора &
символов.
Для пояснения этот вопрос относится к типам данных, где конструкция присваивания или копирования создает неявную поверхностную копию, но при ее модификации создается неявная глубокая копия и применяется к ней изменения вместо исходного объекта.
Причина, по которой я спрашиваю, заключается в том, что я не вижу каких-либо преимуществ использования COW в качестве неявного поведения по умолчанию. Я использую Qt, в котором COW реализован для многих типов данных, практически для всех, которые имеют некоторое динамическое распределение памяти. Но как это может принести пользу пользователю?
Пример:
QString s("some text");
QString s1 = s; // now both s and s1 internally use the same resource
qDebug() << s1; // const operation, nothing changes
s1[o] = z; // s1 "detaches" from s, allocates new storage and modifies first character
// s is still "some text"
Что мы выиграем, используя COW в этом примере?
Если все, что мы собираемся сделать, это использовать константные операции, s1
это избыточно, может также использоваться s
.
Если мы намереваемся изменить значение, то COW только задерживает копирование ресурса до первой неконстантной операции, за счет (хотя и минимальной) стоимости увеличения числа ссылок для неявного совместного использования и отключения от общего хранилища. Похоже, что все накладные расходы, связанные с COW, бессмысленны.
Он не сильно отличается в контексте передачи параметров - если вы не собираетесь изменять значение, передайте как константную ссылку, если вы хотите изменить, вы либо создаете неявную глубокую копию, если не хотите изменять исходный объект, или передайте по ссылке, если вы хотите изменить его. Опять же, COW кажется ненужными накладными расходами, которые ничего не достигают, а лишь добавляет ограничение, что вы не можете изменять исходное значение, даже если хотите, так как любое изменение отсоединит от исходного объекта.
Таким образом, в зависимости от того, знаете ли вы о COW или не обращаете на это внимания, это может привести либо к коду с неясными намерениями и ненужными накладными расходами, либо к полностью запутывающему поведению, которое не соответствует ожиданиям и заставляет вас ломать голову.
Мне кажется, что есть более эффективные и более удобочитаемые решения, хотите ли вы избежать ненужной глубокой копии или намереваетесь ее сделать. Так где же практическая выгода от COW? Я предполагаю, что должна быть некоторая выгода, поскольку в ней используются такие популярные и мощные рамки.
Кроме того, из того, что я прочитал, COW теперь явно запрещено в стандартной библиотеке C ++. Не знаю, имеет ли отношение к этому мошенничество какое-то отношение, но в любом случае, для этого должна быть причина.
[]
оператор. Таким образом, COW допускает плохой дизайн - это не кажется большой выгодой :) Пункт в последнем параграфе кажется верным, но я сам не большой поклонник неявного поведения - люди склонны принимать это как должное, а затем трудно понять, почему код работает не так, как ожидалось, и все время удивляться, пока они не выяснят, что скрывается за скрытым поведением.const_cast
кажется, что она может сломать COW так же легко, как и прервать передачу по константной ссылке. Например,QString::constData()
возвращаетconst QChar *
-const_cast
то и COW рушится - вы будете мутировать данные исходного объекта.char*
очевидно, не знает). Что касается неявного поведения, я думаю, что вы правы, с этим есть проблемы. Дизайн API - это постоянный баланс между двумя крайностями. Слишком неявный, и люди начинают полагаться на особое поведение, как если бы оно было де-факто частью спецификации. Слишком явный, и API становится слишком громоздким, так как вы раскрываете слишком много базовых деталей, которые на самом деле не были важны и внезапно вписываются в вашу спецификацию API.string
классы получили поведение COW, потому что разработчики компилятора заметили, что большая часть кода копирует строки, а не использует const-ссылку. Если бы они добавили COW, они могли бы оптимизировать этот случай и сделать больше людей счастливыми (и это было законно, до C ++ 11). Я ценю их позицию: хотя я всегда передаю свои строки по константной ссылке, я видел весь этот синтаксический мусор, который просто ухудшает читабельность. Я ненавижу писатьconst std::shared_ptr<const std::string>&
только для того, чтобы захватить правильную семантику!Для строк и тому подобного кажется, что это приведет к пессимизации более распространенных вариантов использования, чем нет, поскольку общий случай для строк часто представляет собой небольшие строки, и в этом случае издержки COW будут значительно превышать стоимость простого копирования небольшой строки. Для меня небольшая оптимизация буфера имеет гораздо больше смысла, чтобы в таких случаях избежать выделения кучи вместо копий строк.
Однако, если у вас есть более здоровенный объект, например, андроид, и вы хотите скопировать его и просто заменить его кибернетическую руку, COW кажется вполне разумным способом сохранить изменяемый синтаксис, избегая при этом необходимости глубокого копирования всего Android только для дать копию уникального оружия. Делать его просто неизменным в качестве постоянной структуры данных в этот момент может быть лучше, но «частичное COW», применяемое к отдельным частям Android, кажется разумным для этих случаев.
В таком случае две копии андроида будут иметь один и тот же торс, ноги, ступни, голову, шею, плечи, таз и т. Д. Единственная информация, которая будет отличаться между ними и не будет использоваться, это рука, которая была сделана уникальный для второго андроида при перезаписи его руки.
источник
std::vector<std::string>
прежде чем мы имелиemplace_back
и двигаться семантика в C ++ 11) , Но мы также в основном используем инстансинг. Узловая система может изменять или не изменять данные. У нас есть такие вещи, как сквозные узлы, которые ничего не делают с вводом, а просто выводят копию (они существуют для организации пользователя его программы). В этих случаях все данные копируются для сложных типов ...A
копируется и ничего не делается для объектаB
, это дешевая мелкая копия для сложных типов данных, таких как сетки. Теперь, если мы модифицируемB
, данные, в которые мы модифицируем,B
становятся уникальными благодаря COW, ноA
остаются нетронутыми (за исключением некоторых атомных ссылок).