Когда это не хорошая идея передать ссылкой?

Здесь происходит несколько вещей:

Вы используете кортеж в качестве значения ветвления для if. Достоверность предиката указывает только, является ли он пустым или нет, и ничего о его содержимом:

assert bool(tuple()) is False
assert bool((False, )) is True

Во-вторых, если вы заранее знаете количество элементов в кортеже, используя or s и [ 115] s обычно более читабелен, как вы упомянули:

if is_dog or is_cat:
    do_something()

И, наконец, вы можете использовать any для произвольного числа значений:

values = [is_dog, is_cat, ... more]
if any(values):
    do_something()
5
задан Runcible 26 February 2009 в 05:19
поделиться

9 ответов

Один способ сделать это должно иметь Вашу строку

std::string name;

Как элемент данных Вашего объекта. И затем, в функции unleashMonkeyFish создают строку как Вы, сделал и передают, она ссылкой как Вы показала

void setName( const std::string & parameter_name ) {
    name = parameter_name;
}

Это сделает то, что Вы хотите - создание одной копии скопировать строку в свой элемент данных. Это не похоже на это, должен перераспределить новый буфер внутренне, если Вы присваиваете другую строку. Вероятно, присвоение новой строки просто копирует несколько байтов. станд.:: строка имеет возможность зарезервировать байты. Таким образом, можно звонить "name.reserve (25)"; в Вашем конструкторе и это, вероятно, не перераспределит при присвоении чего-то меньшего. (я сделал тесты, и похоже, что GCC всегда перераспределяет, если Вы присваиваетесь от другого станд.:: строка, но не, если Вы присваиваетесь от струны до. Они говорят, что у них есть строка копии на записи, которая объяснила бы то поведение).

Строка, которую Вы создаете в функции unleashMonkeyFish, автоматически высвободит свои выделенные средства. Это - основная характеристика тех объектов - они управляют своим собственным материалом. Классы имеют деструктор, который они используют для свободных выделенных ресурсов, после того как объекты умирают, станд.:: строка имеет также. По-моему, Вы не должны волноваться о наличии того станд.:: строка, локальная в функции. Это не сделает ничего значимого к Вашей производительности так или иначе, скорее всего. Некоторый станд.:: строковые реализации (msvc ++ afaik) имеют маленько-буферную оптимизацию: Для некоторого маленького предела они сохраняют символы во встроенном буфере вместо того, чтобы выделить от "кучи".

Править:

Как оказалось, существует лучший способ сделать это для классов, которые имеют эффективное swap реализация (постоянное время):

void setName(std::string parameter_name) {
    name.swap(parameter_name);
}

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

obj.setName("Mr. " + things.getName());

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

Для дальнейшего объяснения прочитайте превосходную статью BoostCon09/Rvalue-References

6
ответ дан 18 December 2019 в 06:36
поделиться

Если Вы используете следующее объявление метода:

void setName( const std::string & parameter_name );

затем Вы также использовали бы объявление элемента:

std::string name;

и присвоение в setName тело:

name = parameter_name;

Вы не можете объявить name участник как ссылка, потому что необходимо инициализировать ссылочного участника в конструкторе Object (то, что означает Вас, не могло установить его в setName).

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

5
ответ дан 18 December 2019 в 06:36
поделиться

Просто для уточнения терминология, Вы создали MonkeyFish из "кучи" (использующий новый) и localname на стеке.

Хорошо, таким образом хранить ссылку на объект совершенно законно, но очевидно необходимо знать об объеме того объекта. Намного легче передать строку ссылкой, затем скопируйте в членскую переменную класса. Если строка не является очень большой, или Ваше выполнение этой операции много (и я имею в виду много, много), затем нет действительно никакой потребности волноваться.

Можно ли разъяснить точно, почему Вы не хотите копировать строку?

Править

Альтернативный подход должен создать пул объектов MonkeyName. Каждый MonkeyName хранит указатель на строку. Затем доберитесь, новый MonkeyName путем запроса один от пула (определяет имя на внутренней строке *). Теперь передача, что в класс ссылкой и выполняют прямую подкачку указателя. Конечно, объект MonkayName передал в, изменяется, но если он идет назад в пул, который не будет иметь значения. Единственные издержки являются затем фактической установкой имени, когда Вы получаете MonkeyName от пула.

... надеюсь, что имел некоторый смысл :)

3
ответ дан 18 December 2019 в 06:36
поделиться

Когда компилятор видит...

std::string localname = "Wanda";  

... это будет (запрет волшебства оптимизации) испускают 0x57 0x61 0x6E 0x64 0x61 0x00 [Wanda с пустым разделителем] и хранят его где-нибудь в статический раздел Вашего кода. Затем это вызовет станд.:: строка (символ константы *) и передача это тот адрес. Так как у автора конструктора нет способа знать время жизни предоставленного символа константы *, он или она должен сделать копию. В MonkeyFish:: setName (станд. константы:: строка и), компилятор будет видеть станд.:: строка:: оператор = (станд. константы:: строка и), и, если Ваш станд.:: строка реализована с семантикой копии на записи, компилятор испустит код, чтобы увеличить подсчет ссылок, но не сделать копию.

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

2
ответ дан 18 December 2019 в 06:36
поделиться

Это - точно проблема, которую подсчет ссылок предназначен для решения. Вы могли использовать Повышение shared_ptr <> для ссылки на строковый объект, в некотором роде таким образом, что это живет, по крайней мере, пока каждый указатель в нем.

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

2
ответ дан 18 December 2019 в 06:36
поделиться

Как простое эмпирическое правило хранят Ваши данные как копию в классе, и передачу и данные возврата (константой) ссылка, используют указатели подсчета ссылок по мере возможности.

Я не так обеспокоен копированием нескольких байтов 1000-х строковых данных до такого времени, когда профилировщик говорит, что это - значительная стоимость. OTOH я действительно забочусь, что структуры данных, которые содержат несколько 10-х MBS данных, не становятся скопированными.

2
ответ дан 18 December 2019 в 06:36
поделиться

Вы могли сделать строку в unleashMonkeyFish помехах, но я не думаю, что действительно помогает чему-либо (и могло быть довольно плохим в зависимости от того, как это реализовано).

Я спустился с высокоуровневых языков (как C#, Java) и недавно поразил эту ту же проблему. Я предполагаю, что часто единственный выбор состоит в том, чтобы скопировать строку.

1
ответ дан 18 December 2019 в 06:36
поделиться

При использовании временной переменной для присвоения имени (как в примере кода), необходимо будет в конечном счете скопировать строку в объект MonkeyFish для предотвращения временного строкового объекта идущий конец объема на Вас.

Как Andrew Flanagan упомянул, можно избежать строковой копии при помощи локальной статической переменной или константы.

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

1
ответ дан 18 December 2019 в 06:36
поделиться

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

class MonkeyFish {
public:
  void setName( const std::string & parameter_name ) { name = parameter_name; }

private:
  std::string name;
};

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

2
ответ дан 18 December 2019 в 06:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: