Вручную увеличивая и постепенно уменьшая повышение:: shared_ptr?

Ну, очевидно, Вам нужен способ снять неоднозначность между полями и свойствами. Но действительно необходимы требуемые ключевые слова? Например, ясно, что эти два объявления отличаются:

public int Foo;
public int Bar { }

, Который мог работать. Таким образом, это - синтаксис, который мог очевидно понять компилятор.

, Но тогда Вы добираетесь до ситуации, где пустой блок имеет семантическое значение. Это кажется сомнительным.

5
задан Jesper 28 September 2009 в 08:51
поделиться

4 ответа

В вашем предложении

Клиент будет ответственным за уменьшение счетчика.

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

На самом деле невозможно изменить счетчик shared_ptr ... (гул, в конце я объясню, как ...), но есть другие решения.

Решение 1 : полное владение клиентом

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

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

Решение 2: с обратным вызовом

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

struct Object;

class Pool // may be a singleton, may be synchronized for multi-thread usage
{
public:
  int accept(boost::shared_ptr<Object>); // adds ptr to the map, returns NEW id
  void release(int id) { m_objects.erase(id); }

private:
  std::map< int, boost::shared_ptr<Object> > m_objects;
}; // class Pool

Таким образом, ваш клиент, «уменьшающий» счетчик, на самом деле ваш клиент вызывает обратный вызов метод с идентификатором, который вы использовали, и вы удаляете один shared_ptr :)

Взлом boost :: shared_ptr

Как я уже сказал, возможно (поскольку мы находимся в C ++) действительно взломать shared_ptr. Есть даже несколько способов сделать это.

лучший способ (и самый простой) - просто скопировать файл под другим именем (my_shared_ptr?), А затем:

  • изменить защиту включения
  • включить реальный shared_ptr в начале
  • переименуйте любой экземпляр shared_ptr своим собственным именем (и измените частное на общедоступное для доступа к атрибутам)
  • удалите все, что уже определено в реальном файле, чтобы избежать конфликтов.

Таким образом, вы легко получите собственный shared_ptr, для которого вы можете получить доступ к счетчику. Это не решает проблему прямого доступа кода C к счетчику, хотя вам, возможно, придется `` упростить '' код здесь, чтобы заменить его встроенным (который работает, если вы не многопоточны, и совершенно катастрофически если да).

Я намеренно не упомянул трюк с 'reinterpret_cast', а указатель смещает их. Существует так много способов получить незаконный доступ к чему-либо на C / C ++!

Могу я посоветовать вам НЕ использовать хаки? Двух представленных выше решений должно быть достаточно для решения вашей проблемы.

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

Возможно, вы используете boost :: shared_ptr через границы DLL, что не будет работать должным образом. В этом случае вам может помочь boost :: intrusive_ptr . Это частый случай неправильного использования shared_ptr , люди пытаются обойтись грязными хаками ... Возможно, я ошибаюсь в вашем случае, но не должно быть веских причин делать то, что вы пытаетесь сделать ;-)

ДОБАВЛЕНО 07/2010: Проблемы, похоже, больше связаны с загрузкой / выгрузкой DLL, чем с самим shared_ptr. Даже логика ускорения мало что говорит о тех случаях, когда boost :: intrusive_ptr следует предпочесть shared_ptr . Я переключился на разработку .NET и не следил за подробностями TR1 по этой теме, поэтому будьте осторожны, этот ответ может быть больше недействительным ...

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

Здесь следует разделить задачи: если клиент передает необработанный указатель, клиент будет нести ответственность за управление памятью (т.е. очистку впоследствии). Если вы создаете указатели, вы будете нести ответственность за управление памятью. Это также поможет вам с проблемами границ DLL, которые были упомянуты в другом ответе.

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

1. Ручка?

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

Ниже я предположу, что для простоты вы передадите пользователю указатель объекта.

2. acquire и unacquire ?

Вы должны создать класс менеджера, как описано Matthieu M. в его ответе, чтобы запоминать, что было приобретено/не приобретено пользователем.

Поскольку инферфейс - это C, вы не можете ожидать, что он будет использовать delete или что-то еще. Поэтому заголовок типа:

#ifndef MY_STRUCT_H
#define MY_STRUCT_H

#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus

typedef struct MyStructDef{} MyStruct ; // dummy declaration, to help
                                        // the compiler not mix types

MyStruct * MyStruct_new() ;
size_t     MyStruct_getSomeValue(MyStruct * p) ;
void       MyStruct_delete(MyStruct * p) ;

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // MY_STRUCT_H

позволит пользователю использовать ваш класс. Я использовал объявление фиктивной структуры, потому что хочу помочь пользователю C, не навязывая ему использование общего указателя void *. Но использование void * все равно хорошо.

Исходный текст на C++, реализующий эту возможность, будет выглядеть так:

#include "MyClass.hpp"
#include "MyStruct.h"

MyManager g_oManager ; // object managing the shared instances
                       // of your class

extern "C"
{

MyStruct * MyStruct_new()
{
   MyClass * pMyClass = g_oManager.createMyClass() ;
   MyStruct * pMyStruct = reinterpret_cast<MyStruct *>(pMyClass) ;
   return pMyStruct ;
}

size_t MyStruct_getSomeValue(MyStruct * p)
{
   MyClass * pMyClass = reinterpret_cast<MyClass *>(p) ;

   if(g_oManager.isMyClassExisting(pMyClass))
   {
      return pMyClass->getSomeValue() ;
   }
   else
   {
      // Oops... the user made a mistake
      // Handle it the way you want...
   }

   return 0 ;
}

void MyStruct_delete(MyStruct * p)
{
   MyClass * pMyClass = reinterpret_cast<MyClass *>(p) ;
   g_oManager.destroyMyClass(pMyClass) ;
}

}

Обратите внимание, что указатель на MyStruct является просто недействительным. Вы не должны использовать его по какой-либо причине, не переинтерпретировав_каст в его исходный тип MyClass (более подробную информацию об этом см. в ответе Джаифа. Пользователь языка C будет использовать его только с соответствующими функциями MyStruct_*.

Обратите также внимание, что этот код проверяет существование класса. Это может быть излишеством, но это возможное использование менеджера (см. ниже)

3. О менеджере

Менеджер будет хранить, как предложил Matthieu M., карту, содержащую общий указатель в качестве значения (и сам указатель, или хэндл, в качестве ключа). Или мультикарту, если пользователь может каким-то образом получить один и тот же объект несколько раз.

Хорошим моментом в использовании менеджера будет то, что ваш C++ код сможет отследить, какие объекты были неправильно "неприобретены" пользователем (добавление информации в методы acquire/unacquire типа __FILE__ и __LINE__ может помочь сузить круг поиска ошибок).

Таким образом, менеджер сможет:

  1. НЕ освобождать несуществующий объект (как, кстати, пользователь C смог его приобрести?)
  2. ЗНАТЬ в конце выполнения, какие объекты не были невостребованными
  3. В случае невостребованных объектов, уничтожить их в любом случае (что хорошо с точки зрения RAII). Это несколько лукаво, но вы могли бы предложить вот это
  4. Как показано в коде выше, это даже может помочь обнаружить, что указатель не указывает на правильный класс
3
ответ дан 18 December 2019 в 06:35
поделиться
Другие вопросы по тегам:

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