Существует ли какой-либо независимый от компилятора и синтаксически элегантный способ установки указателя vtable в объекте, выделенном с помощью malloc?
Я не могу использовать новое напрямую, поскольку мне нужно иметь возможность контролировать поток выпусков памяти по требованию, что требует использования void ptrs для хранения областей памяти в диспетчере памяти, пока не будет достаточно времени для освобождения.
class AbstractData
{
public:
AbstractData() {}
virtual ~AbstractData() {}
protected:
virtual void SetData(int NewData) =0;
virtual int GetData() const =0;
};
class ConcreteData : public AbstractData
{
protected:
int Data;
public:
ConcreteData() {}
~ConcreteData() {}
void SetData(int NewData);
int GetData() const;
};
void ConcreteData::SetData(int NewData) {Data=NewData;}
int ConcreteData::GetData() const
{return Data;}
int main(int argc, char* argv[])
{
int OBJ_NUMBER = 4;
ConcreteData* Test = (ConcreteData*)malloc(OBJ_NUMBER*sizeof(ConcreteData));
if (!Test)
return -1;
for (int x = 0; x < OBJ_NUMBER; x++)
Test[x] = ConcreteData();
Test[0]->GetData(); //Constructor was never called, vptr never initialized, crash
free(Test);
Test = NULL;
}
Я надеялся, что локальная копия будет иметь инициализированный указатель vtable, но, увы, это не так.
Я знаю, что вы можете выполнить разыменование, зависящее от компилятора, со смещением vptr, если вы знаете, где оно находится, но это решение зависит от компилятора и неэффективно для использования во многих распределениях. Пример работы с MSVC ++ 8.0
int main(int argc, char* argv[])
{
int OBJ_NUMBER = 4;
ConcreteData* Test = (ConcreteData*)malloc(OBJ_NUMBER*sizeof(ConcreteData));
if (!Test)
return -1;
ConcreteData StealVPtr();
int* VPtr = *(int**)StealVPtr;
for (int x = 0; x < OBJ_NUMBER; x++)
*(int**)Test[x] = VPtr;
Test[0]->GetData(); //VPtr initialized in compiler dependent way
free(Test);
Test = NULL;
}
В качестве альтернативы можно использовать новое размещение, но, опять же, оно выглядит синтаксически неэлегичным и может вызвать проблемы смещения массива типов с деструкторами, когда оно добавляет счетчик массива перед ptr.
int main(int argc, char* argv[])
{
int OBJ_NUMBER = 4;
ConcreteData* Test = (ConcreteData*)malloc(OBJ_NUMBER*sizeof(ConcreteData));
if (!Test)
return -1;
for (int x = 0; x < OBJ_NUMBER; x++)
{
if (!(ConcreteData* PlcTest = new(Test[x]) ConcreteData()))
{
free(Test);
Test = NULL;
return -1;
}
}
PlcTest[0]->GetData(); //Constructor was invoked and VPtr was initialized
for (int x = OBJ_NUMBER-1; x >= 0; x--)
PlcTest[x].~ConcreteData();
PlcTest = NULL;
free(Test);
Test = NULL;
}
Действительно ли это единственный способ инициализации конструкторов VTable ptr / call для объектов, использующих malloc?