Зачем нужен null shared ptr и как его можно использовать?

у Скотта Мейерса Эффективный C++, пункт 18 сделать интерфейсы простыми в использовании правильно и трудно использовать неправильно, он упомянул нулевой shared_ptr:

std::tr1::shared_ptr<Investment> pInv(static_cast<Investment*>(0), getRidOfInvestment)

и операция назначения vogue

pInv = ...     //make retVal point to the correct object

в этом случае может потребоваться создать нулевой shared_ptr и выполнить назначение позже? Почему бы просто не создать shared_ptr, когда у вас есть ресурсы (сырой указатель)?

так как Скотт Мейерс не показал полное назначение в предыдущем примере я думал, что оператор assign shared_ptr перегружен, что можно сделать это:

pInv = new Investment;    // pInv will take charge of the pointer
                          // but meanwhile keep the delete function it already had

но я пытался с boostреализация это не работает таким образом. Тогда какой смысл иметь null shared_ptr?

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

ps. подробнее об инициализации и присваивания shared_ptr

#include <boost/shared_ptr.hpp>

int main(int argc, char *argv[])
{
    boost::shared_ptr<int> ptr1(new int);
    boost::shared_ptr<int> ptr2;
    ptr2.reset(new int);
    boost::shared_ptr<int> ptr3 = new int;

    return 0;
}

этот пример не может быть составлен g++ (Ubuntu / Linaro 4.5.2-8ubuntu4) 4.5.2 и последний толчок:

sptr.cpp: In function ‘int main(int, char**)’:
sptr.cpp:8:39: error: conversion from ‘int*’ to non-scalar type ‘boost::shared_ptr<int>’    requested

3 ответов


нет необходимости использовать этот хак, чтобы получить null (пустой) shared_ptr. Просто используйте конструктор по умолчанию:

std::shared_ptr<Investment> pInv; // starts null

назначить указатель на shared_ptr, либо сделать это во время строительства:

std::shared_ptr<Investment> pInt(new Investment);
// not allowed due to explicit annotation on constructor:
// std::shared_ptr<Investment> pInt = new Investment;

или использовать .reset() функция:

pInt.reset(new Investment);

возможно, что автор этой статьи, возможно, намеревался предоставить пользовательский делетер (getRidOfInvestment). Однако функция deleter сбрасывается, когда .reset() вызывается, или когда иначе внутренняя указатель изменен. Если вы хотите пользовательский делетер, вы должны передать его .reset() при создании shared_ptr.

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

class Investment {
protected:
  Investment();
  // ...
public:
  static shared_ptr<Investment> create();
};

shared_ptr<Investment> Investment::create() {
  return shared_ptr<Investment>(new Investment, getRidOfInvestment);
}

позже:

shared_ptr<Investment> pInv = Investment::create();

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


это та же причина иметь нулевой необработанный указатель-например,

скажите, что у вас есть:

typedef std::tr1::shared_ptr<Investment> InvestmentPtr;
map<key,InvestmentPtr> portfolio;
...
get(mykey) {
  iterator it = portfolio.find(mykey);
  if (it == portfolio.end()) 
    return InvestmentPtr();
  else 
    return it->second;
  }
}

Это позволяет делать:

InvestmentPtr p = get(key);
if (p) ...

существует множество причин, по которым объекты могут быть конструктивными по умолчанию. Прежде всего, вы хотели бы, чтобы смарт-указатель был максимально похож на необработанный указатель, и так как вы можете сказать int * p; (и получить неопределенный, неинициализированный указатель), вы также можете сказать shared_ptr<int> p; и получите указатель, который никуда не указывает (но вы можете проверить его с помощью !).

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