Как получить указатель на shared_ptr? - PullRequest
5 голосов
/ 03 марта 2010

Сейчас я взламываю старый код на C, попробуйте сделать его более в стиле C ++ / Boost:

есть функция выделения ресурсов, выглядит так:

my_src_type* src;
my_src_create(&src, ctx, topic, handle_src_event, NULL, NULL);

я пытаюсь обернуть src в shared_ptr:

shared_ptr<my_src_type> pSrc;

Я забыл упомянуть только сейчас. Мне нужно сделать это как цикл

std::map<string, shared_ptr<my_src_type>  > dict;
my_src_type* raw_ptr;

BOOST_FOREACH(std::string topic, all_topics)
{
    my_src_create(&raw_ptr, ctx, topic, handle_src_event, NULL, NULL);
    boost::shared_ptr<my_src_type> pSrc(raw_ptr);
    dict[topic] = pSrc;
}

Могу ли я сделать это так?

Ответы [ 6 ]

8 голосов
/ 04 марта 2010

Использование shared_ptr на ресурсах в стиле C

С помощью boost::shared_ptr вы можете передать указатель функции «удалителю», который будет вызываться автоматически, когда счетчик ссылок достигнет нуля. Эта функция позволяет использовать shared_ptr для управления ресурсами, возвращаемыми устаревшими API-интерфейсами C.

Подумайте о том, чтобы оставить свое прежнее my_src_create нетронутым, и предоставьте новую "фабричную" функцию, которая возвращает shared_ptr:

void my_src_deleter(my_src_type* raw_ptr)
{
    my_src_destroy(raw_ptr);
}

typedef boost::shared_ptr<my_src_type> my_src_shared_ptr;

my_src_shared_ptr create_my_src(...)
{
    my_src_type* raw_ptr;
    my_src_create(&raw_ptr, ctx, topic, handle_src_event, NULL, NULL);
    return my_src_shared_ptr(raw_ptr, &my_src_deleter);
}

std::map<string, my_src_shared_ptr> dict;

BOOST_FOREACH(std::string topic, all_topics)
{
    dict[topic] = create_my_src(ctx, topic, handle_src_event, NULL, NULL);
}

Упаковка устаревшей структуры / функций C в классе

В качестве альтернативы (как предложил jpalecek) вы можете обернуть my_src в классе. Создание и уничтожение устаревших my_src объектов осуществляется в конструкторе и деструкторе. Если вы собираетесь это сделать, вам следует подумать о том, хотите ли вы, чтобы ваш класс MySrc был копируемым. Если MySrc является тяжеловесным или дорогим в создании, вы, вероятно, захотите сделать его не подлежащим копированию и рассмотрите возможность использования shared_ptr<MySrc>, если будет общее владение MySrc:

class MySrc
{
public:
    typedef boost::shared_ptr<MySrc> Ptr;
    MySrc(...) { my_src_create(&src_, ...); }
    ~MySrc() { my_src_destroy(&src_); }
    // Other member functions that uses my_src legacy functions

private:
    my_src_type* src_;
    // Make copy-constructor and assignment private to disallow copies
    MySrc(const MySrc& rhs) {}
    MySrc& operator=(const MySrc& rhs) {return *this;}
};

std::map<string, MySrc::Ptr> dict;

BOOST_FOREACH(std::string topic, all_topics)
{
    dict[topic] = MySrc::Ptr(
        new MySrc(ctx, topic, handle_src_event, NULL, NULL) );
}

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

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

2 голосов
/ 03 марта 2010

Не думаю, что ваши вопросы имеют большой смысл. Если my_src_create переделывается, то передайте ссылку на общий указатель или вместо этого верните общий указатель. Если вы не дорабатываете этот метод, вы не сможете этого сделать. Я рекомендую использовать необработанный указатель для создания и затем обернуть его в общий указатель:

shared_ptr<my_src_type> src;
{
   my_src_type* raw_src;
   my_src_create(&raw_src, ctx, topic, handle_src_event, NULL, NULL);
   src.reset( raw_src ); // hand ownership to shared_ptr
}

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

2 голосов
/ 03 марта 2010

номер

По сути, вы должны сделать это по-старому, а затем как-то преобразовать результат в shared_pointer.

Вы можете сделать это, просто инициализируя shared_pointer

my_src_type* pSrc;
my_src_create(&src, ctx, topic, handle_src_event, NULL, NULL);
shared_ptr<my_src_type> sp(pSrc);

но будьте осторожны, это не удастся, если функция my_src_create может вернуть уже существующий объект. Кроме того, если есть функция my_src_destroy, она не будет вызываться.

ИМХО, самый чистый способ - обернуть вашу структуру в класс C ++:

class MySrc {
  my_src_type* pSrc;
public:
  MySrc(...) { my_src_create(&pSrc, ...); }
  ~MySrc() { my_src_destroy(&pSrc); }
private:
  MySrc(const MySrc&);
  void operator=(const MySrc&); // disallow copying
};

, а затем используйте общий указатель для MySrc обычного способа.

0 голосов
/ 03 марта 2010

Я бы использовал этот код для решения вашей проблемы:

my_src_type* src;
my_src_create(&src, ctx, topic, handle_src_event, NULL, NULL);
boost::shared_ptr<my_src_type> pSrc(src);

С этого момента pSrc будет управлять выделенной памятью, обозначенной my_src_type* src.

РЕДАКТИРОВАТЬ: удаление ошибочной части моего ответа.

0 голосов
/ 03 марта 2010

Вы всегда можете сделать это «по-старому», а затем назначить указатель src на shared_ptr, используя reset () или make_shared () .

0 голосов
/ 03 марта 2010

Ты тоже переделываешь my_src_create()?

Учитывая имя функции, я бы возвратил boost :: shared_ptr вместо того, чтобы передавать предварительно сконструированный, так как это намного яснее и чище ИМХО. Если только функция не возвращает какой-либо код ошибки / успеха, но это было бы лучше, если бы было сгенерировано исключение.

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

...