Ну, очевидно, Вам нужен способ снять неоднозначность между полями и свойствами. Но действительно необходимы требуемые ключевые слова? Например, ясно, что эти два объявления отличаются:
public int Foo;
public int Bar { }
, Который мог работать. Таким образом, это - синтаксис, который мог очевидно понять компилятор.
, Но тогда Вы добираетесь до ситуации, где пустой блок имеет семантическое значение. Это кажется сомнительным.
В вашем предложении
Клиент будет ответственным за уменьшение счетчика.
означает, что данный клиент отвечает за управление памятью, и что вы ему доверяете. Я до сих пор не понимаю, почему.
На самом деле невозможно изменить счетчик 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, для которого вы можете получить доступ к счетчику. Это не решает проблему прямого доступа кода C к счетчику, хотя вам, возможно, придется `` упростить '' код здесь, чтобы заменить его встроенным (который работает, если вы не многопоточны, и совершенно катастрофически если да).
Я намеренно не упомянул трюк с 'reinterpret_cast', а указатель смещает их. Существует так много способов получить незаконный доступ к чему-либо на C / C ++!
Могу я посоветовать вам НЕ использовать хаки? Двух представленных выше решений должно быть достаточно для решения вашей проблемы.
Возможно, вы используете boost :: shared_ptr через границы DLL, что не будет работать должным образом. В этом случае вам может помочь boost :: intrusive_ptr . Это частый случай неправильного использования shared_ptr
, люди пытаются обойтись грязными хаками ... Возможно, я ошибаюсь в вашем случае, но не должно быть веских причин делать то, что вы пытаетесь сделать ;-)
ДОБАВЛЕНО 07/2010: Проблемы, похоже, больше связаны с загрузкой / выгрузкой DLL, чем с самим shared_ptr. Даже логика ускорения мало что говорит о тех случаях, когда boost :: intrusive_ptr
следует предпочесть shared_ptr
. Я переключился на разработку .NET и не следил за подробностями TR1 по этой теме, поэтому будьте осторожны, этот ответ может быть больше недействительным ...
Здесь следует разделить задачи: если клиент передает необработанный указатель, клиент будет нести ответственность за управление памятью (т.е. очистку впоследствии). Если вы создаете указатели, вы будете нести ответственность за управление памятью. Это также поможет вам с проблемами границ DLL, которые были упомянуты в другом ответе.
Если вы хотите максимальной безопасности, дайте пользователю ручку, а не указатель. Таким образом, он не сможет попытаться освободить
его и наполовину преуспеть.
Ниже я предположу, что для простоты вы передадите пользователю указатель объекта.
Вы должны создать класс менеджера, как описано 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_*.
Обратите также внимание, что этот код проверяет существование класса. Это может быть излишеством, но это возможное использование менеджера (см. ниже)
Менеджер будет хранить, как предложил Matthieu M., карту, содержащую общий указатель в качестве значения (и сам указатель, или хэндл, в качестве ключа). Или мультикарту, если пользователь может каким-то образом получить один и тот же объект несколько раз.
Хорошим моментом в использовании менеджера будет то, что ваш C++ код сможет отследить, какие объекты были неправильно "неприобретены" пользователем (добавление информации в методы acquire/unacquire типа __FILE__
и __LINE__
может помочь сузить круг поиска ошибок).
Таким образом, менеджер сможет: