Я использую один из следующих двух контейнеров, когда предпочтительным является дружественное взаимодействие со сборщиком мусора:
template<typename T> ref class GcPlainPtr sealed {
T* ptr;
public:
GcPlainPtr(T*ptr): ptr(ptr) { GC::AddMemoryPressure(sizeof(T)); }
!GcPlainPtr() {
GC::RemoveMemoryPressure(sizeof(T));
delete ptr; ptr = nullptr;
}
~GcPlainPtr() { this->!GcPlainPtr(); } //mostly just to avoid C4461
T* get() {return ptr;}
static T* operator->(GcPlainPtr<T>% gcPtr) { return gcPtr.ptr;}
static operator T*(GcPlainPtr<T>% gcPtr) { return gcPtr.ptr; }
};
Предыдущий контейнер выглядит достаточно для ваших нужд. Вы можете использовать его следующим образом:
ref class MyManagedClass {
GcPlainPtr<userData> myUserData;
MyManagedClass(...bla...)
: myUserData(new userData(...))
, ...
{...}
AnotherMethod() {
std::cout << myUserData->data1 << '\n';
AddItem(1, myUserData.get());
}
}
Преимущество предыдущего подхода состоит в том, что даже если вы забудете избавиться от объектов, нагрузка на память будет разумно обновлена, так что сбор мусора будет происходить с соответствующей частотой.
Если вам известен размер элемента данных, который вы распределяете строго, но это не просто прямой размер (т. Е. Собственная структура или класс выделяет память внутри), следующий вариант может быть более подходящим:
template<typename T> ref class GcAutoPtr sealed {
T* ptr;
size_t size;
public:
GcAutoPtr(T*ptr,size_t size) : ptr(ptr), size(size) {
GC::AddMemoryPressure(size);
}
!GcAutoPtr() {
GC::RemoveMemoryPressure(size);
size=0;
delete ptr;
ptr = nullptr;
}
~GcAutoPtr() { this->!GcAutoPtr();} //mostly just to avoid C4461
T* get() {return ptr;}
static T* operator->(GcAutoPtr<T>% gcPtr) { return gcPtr.ptr;}
static operator T*(GcAutoPtr<T>% gcPtr) { return gcPtr.ptr; }
};