Вопросы об использовании shared_ptr - С++

У меня мало вопросов о лучших методах использования shared_ptr.

Вопрос 1

Является ли копирование shared_ptr дешевым? Или мне нужно передать его в качестве ссылки на мои собственные вспомогательные функции и вернуть в качестве значения? Что-то вроде,

void init_fields(boost::shared_ptr<foo>& /*p_foo*/);
void init_other_fields(boost::shared_ptr<foo>& /*p_foo*/);

boost::shared_ptr<foo> create_foo()
{
    boost::shared_ptr<foo> p_foo(new foo);
    init_fields(p_foo);
    init_other_fields(p_foo);
}

Вопрос 2

Должен ли я использовать boost::make_shared для построения shared_ptr? Если да, то какие преимущества он предлагает? И как мы можем использовать make_shared, когда T не имеет конструктора без параметров?

Вопрос 3

Как использовать const foo*? Я нашел два подхода для этого.

void take_const_foo(const foo* pfoo)
{

}

int main()
{
    boost::shared_ptr<foo> pfoo(new foo);
    take_const_foo(pfoo.get());
    return 0;
}

ИЛИ

typedef boost::shared_ptr<foo> p_foo;
typedef const boost::shared_ptr<const foo> const_p_foo;

void take_const_foo(const_p_foo pfoo)
{

}

int main()
{
     boost::shared_ptr<foo> pfoo(new foo);
     take_const_foo(pfoo);
     return 0;
}

Вопрос 4

Как я могу вернуться и проверить NULL на объект shared_ptr? Это что-то вроде:

boost::shared_ptr<foo> get_foo()
{
     boost::shared_ptr<foo> null_foo;
     return null_foo;
}

int main()
{
     boost::shared_ptr<foo> f = get_foo();
     if(f == NULL)
     {
          /* .. */
     }
     return 0;
}

Любая помощь будет отличной.

Ответы

Ответ 1

На большинство вопросов был дан ответ, но я не согласен с тем, что копия shared_ptr дешева.

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

С точки зрения производительности обычно лучше использовать контейнер указателя ускорения вместо контейнера shared_ptr.

Ответ 2

  • Копирование дешево, указатель не занимает много места. Все дело в том, чтобы сделать его маленьким, чтобы разрешить использование в контейнерах по значению (например, std::vector< shared_ptr<Foo> >).

  • make_shared принимает переменное количество параметров и является предпочтительной механикой над ее построением (точно так же, как make_pair). Преимущество - читаемость, особенно если задействованы временные и/или пространства имен:

  • boost:: const_ptr_cast как уже было предложено

  • интеллектуальные указатели имеют перегруженные операторы и могут быть непосредственно использованы в выражениях, оцененных как bool. Не используйте get. Для всего. Вместо сравнения p.get со всем, сравните пустой экземпляр указателя (my_ptr != boost::shared_ptr< MyClass >())

AD.2

func_shared( boost::shared_ptr<my_tools::MyLongNamedClass>( 
    new my_tools::MyLongNamedClass( param1, param2 ) );

против

func_shared( boost::make_shared<my_tools::MyLongNamedClass>( param1, param2 ));

Ответ 3

  • Да, копия абсолютно дешевая. Помимо удержания указателя, есть (обычно) еще один член данных для класса shared_ptr - счет использования.
  • Не могу ответить на это, я обычно использую boost-версии до того, как был введен make_shared (1.40?)
  • Использовать boost:: const_pointer_cast
  • shared_ptr имеет оператор ==/!= определенный. В приведенном выше примере: if (f)

Ответ 4

  • Копирование shared_ptr теперь стоит 32 байта в копии стека и дополнительных приращениях/сокращениях refcount. Решите, дешево ли это для вас или нет, но я не вижу причин, почему бы не передать ссылку на const, особенно, что у вас уже есть typedef для ptr: void f(const foo_ptr &myfoo) особенно учитывая, что стандартный параметр разрешения отсутствия записи-записи в С++ является ссылкой на константу.

  • Я бы предпочел не иметь никаких функций, которые принимают указатель, который не является общим. Это похоже (хотя и не идентично) семантике передачи параметров в Java и С#. Почему нужно каждый раз решать, как передать объект, а не иметь один стандартный способ его выполнения?

  • Используйте if(p) так же, как для обычных указателей. Булева семантика преобразования довольно аккуратная.

Ответ 5

  1. Одной из основных причин существования shared_ptr является относительно дешевая копия.
  2. Существуют версии make_shared, которые принимают параметры (и если ваш компилятор поддерживает Variadic templates, тот, который принимает список переменных).
  3. Похоже, вы ищете const_ptr_cast?
  4. Чтобы вернуть нулевой указатель, вы можете передать '0' в share_ptr ctor. Чтобы проверить нулевой указатель, вы можете сравнить p.get() с 0.