Объединение двух блоков памяти в C ++?

Я пытаюсь сделать свой собственный распределитель памяти в C ++ для образовательных целей, и у меня есть такой код:

class IntObj
{
public:
IntObj(): var_int(6) {}

void setVar(int var)
{
var_int = var;
}

int getVar()
{
return var_int;
}

virtual size_t getMemorySize()
{
return sizeof(*this);
}
int a = 8;

~IntObj()
{}
private:
int var_int;
};

И я застрял в том, как объединить неиспользуемые фрагменты памяти. Я пытаюсь проверить это так:

char *pz = new char[sizeof(IntObj) * 2]; //In MacOS, IntObj takes 16 bytes
char *pz2 = &pz[sizeof(IntObj)]; // Take address of 16-th cell
char *pz3 = new char[sizeof(IntObj) / 2]; //Array of 8 bytes

char **pzz = &pz2;
pzz[sizeof(IntObj)] = pz3; // Set address of cell 16 to the pz3 array
new (&pzz) IntObj; //placement new

IntObj *ss = reinterpret_cast<IntObj *>(&pzz);
cout << ss->a;

Выход 8, как и ожидалось. Мои вопросы:

  • Почему вывод правильный?
  • Правильный ли этот код? Если нет, есть ли другие способы реализации объединения двух блоков памяти?

ОБНОВЛЕНИЕ: Все методы работают правильно.
например, это будет работать:

ss->setVar(54);
cout << ss->getVar();

Выход составляет 54.

ОБНОВЛЕНИЕ 2: Прежде всего, моя задача состоит не в том, чтобы запросить новый блок памяти у ОС для создания экземпляра объекта, а в том, чтобы выдать его из связанного списка свободных блоков (которые были выделены при запуске программы). Моя проблема в том, что у меня могут быть полиморфные объекты с различными размерами, и я не знаю, как разделить блоки памяти или объединить (это я понимаю, объединяя или объединяя куски) их (если выделение запрошено) эффективно.

0

Решение

Reinterpret_cast не выполняет никакой проверки, он просто доверяет операции и принудительно выполняет ее.

Поскольку вы выделили достаточный объем памяти, ошибки сегментации не возникает, однако проблемы не ограничиваются этим. Ваши операции с указателями содержат неверный псевдоним, что приводит к непонятному редактированию памяти.

С reinterpret_cast вы можете, например, иметь исходный объект, который наследует от 2 объектов, а объект после приведения все еще наследует от тех же 2 объектов, но когда вы пытаетесь получить доступ к членам после приведения, вы можете подумать, что вы обращаетесь к той же переменной из-за названия, но вы можете получить доступ к различным элементам!

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

Если компилятор жалуется на static_cast, dynamic_cast может работать во время выполнения, но это будет более дорогостоящим. Также вы должны проверить, вернул ли он значение null, если он не был успешным.

Функции-члены работали, потому что они не хранятся внутри объекта, а определены для него. Литерал 8, скорее всего, является совпадением, поскольку в отношении выполненных недопустимых операций с указателями, поскольку вы, очевидно, не устанавливаете его перед преобразованием, а reinterpret_cast не запускается как конструктор, а просто изменяет тип указателя, полагая, что это можно сделать.

Один блок памяти переосмысливается и разделяется:

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

Также обратите внимание на BLOB-объекты, изначально используемые для возможности анализа вашей собственной необработанной памяти в данных. Перечисления (часто анонимные) часто используются в качестве политик.

Непересекающиеся блоки:

Вы можете обнаружить, что у объекта есть указатель на другой объект. Это делает другой кусок памяти, на который ссылаются и возможно доступный этим. Если вы используете союзы или разбираете BLOB-объекты или приводите исходный объект к другому объекту, вы должны быть уверены, что указатель остается на месте и действителен. Это то, что происходит в повседневной C ++, когда у класса есть члены-указатели, и мы конструируем объекты и затем указываем на них указатели. Это непересекающиеся куски памяти. Статическое и динамическое приведение к действительным классам, которые могут быть преобразованы, сохранит указатели.

0

Другие решения

Здесь есть ряд недоразумений

char *pz = new char[sizeof(IntObj) * 2];  // fine
char *pz2 = &pz[sizeof(IntObj)];          // fine
char *pz3 = new char[sizeof(IntObj) / 2]; // fine
char **pzz = &pz2;                        // fine
pzz[sizeof(IntObj)] = pz3;                // bad

pzz это указатель, который указывает только на один char*, которая является переменной pz2, Это означает, что любой доступ или изменение прошлого pzz[0] неопределенное поведение (очень плохое). Скорее всего, вы изменяете содержимое какой-то другой переменной.

new (&pzz) IntObj; // questionable

Это строит IntObj в пространстве переменная pzzне где pzz указывает на. Конструктор наборов курсов a в 8 тем самым топая по содержимому pzz (это не будет указывать на pz2 больше). Я не уверен, что это само по себе является неопределенным поведением (так как там было бы место для целого IntObj), но с помощью Это определенно:

IntObj *ss = reinterpret_cast<IntObj *>(&pzz); // bad

Это нарушает правило строгого наложения имен. В то время как стандарт щедр char* псевдонимы, это делает не разрешать char** в IntObj* псевдонимы. Это демонстрирует более неопределенное поведение.

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

3