На VTable указатели и malloc - PullRequest
       14

На VTable указатели и malloc

0 голосов
/ 09 октября 2011

Существует ли какой-либо независимый от компилятора и синтаксически элегантный способ установки указателя 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?

Ответы [ 2 ]

0 голосов
/ 09 октября 2011

Вы можете использовать оператор размещения для создания экземпляров классов в предварительно выделенной памяти.

0 голосов
/ 09 октября 2011

Возможно, вы не знаете, что можете переопределить глобальные new и delete. Обратите внимание, вам также нужно переопределить new[] и delete[], чтобы завершить

Вот пример:

void * operator new( size_t size ) 
{
    return super_malloc( size );
}
...