Скопировать или создать shared_ptr? - PullRequest
0 голосов
/ 25 апреля 2019

Рассмотрим некоторый источник данных, который содержит shared_ptr (например, член структуры).Если у вас есть гарантия, что это не временное значение, а будет действительным в текущей области, и вы хотите иметь псевдоним этого указателя: что более производительно, скопируйте shared_ptr или имеющую (возможно, const) ссылку наоригинал?

Пример:

struct S {
  shared_ptr<T> ptr;
};

void fun(S s) {
  shared_ptr<T> alias1 = s.ptr;
  shared_ptr<T> const& alias2 = s.ptr;
  /* do something with the alias */
}

Редактировать: Мотивация. Это может быть необходимо, если, например, 1) получение s.ptr включает обход цепочки вызовов функцийили derefs или 2) желая улучшить удобочитаемость.

Tradeoff. Копирование указателя кажется более «правильным» в том смысле, что оно имитирует то, что вы делаете с необработанным указателем, но это требуетмеханизм подсчета, чтобы сделать свое дело (на строительство и разрушение).С другой стороны, наличие ссылки, которую вы часто используете, влечет за собой дополнительную разыменование, которое, в свою очередь, стоит дорого (разыменование находится поверх всего, что вы делаете с удерживаемым объектом T).

IsЕсть общее эмпирическое правило, что является более производительным?

Ответы [ 2 ]

2 голосов
/ 26 апреля 2019

Эмпирическое правило

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

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

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

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

А как насчет темы?

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

Копирование shared_ptr намного дешевле, чем запуск потока в любом случае, поэтому его издержки минимальны.

0 голосов
/ 29 апреля 2019

Sr. Перес прав насчет лучшего способа использовать общие указатели.

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

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

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

Что такое оптимизация копирования и возврата значения?

Если у вас не получилось использовать совместно используемый указатель, вы можете превратить его в обычный указатель с помощью get ().

Конечно, это делает код менее безопасным, но кому нужна скучная жизнь:)

#include <memory>

struct S {
  std::shared_ptr<int> ptr;
};

void fun(S s) {
    int *alias = s.ptr.get();
    *alias = 1;
}

Godbolt:

fun(S):                               # @fun(S)
    mov     rax, qword ptr [rdi]
    mov     dword ptr [rax], 1
    ret
...