Я пытаюсь сделать свой собственный распределитель памяти в 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: Прежде всего, моя задача состоит не в том, чтобы запросить новый блок памяти у ОС для создания экземпляра объекта, а в том, чтобы выдать его из связанного списка свободных блоков (которые были выделены при запуске программы). Моя проблема в том, что у меня могут быть полиморфные объекты с различными размерами, и я не знаю, как разделить блоки памяти или объединить (это я понимаю, объединяя или объединяя куски) их (если выделение запрошено) эффективно.
Reinterpret_cast не выполняет никакой проверки, он просто доверяет операции и принудительно выполняет ее.
Поскольку вы выделили достаточный объем памяти, ошибки сегментации не возникает, однако проблемы не ограничиваются этим. Ваши операции с указателями содержат неверный псевдоним, что приводит к непонятному редактированию памяти.
С reinterpret_cast вы можете, например, иметь исходный объект, который наследует от 2 объектов, а объект после приведения все еще наследует от тех же 2 объектов, но когда вы пытаетесь получить доступ к членам после приведения, вы можете подумать, что вы обращаетесь к той же переменной из-за названия, но вы можете получить доступ к различным элементам!
Более того, если вы переинтерпретируете обратно, в отличие от static_cast, он не обязательно вернет тот же указатель, хотя он будет указывать на ту же память.
Если компилятор жалуется на static_cast, dynamic_cast может работать во время выполнения, но это будет более дорогостоящим. Также вы должны проверить, вернул ли он значение null, если он не был успешным.
Функции-члены работали, потому что они не хранятся внутри объекта, а определены для него. Литерал 8, скорее всего, является совпадением, поскольку в отношении выполненных недопустимых операций с указателями, поскольку вы, очевидно, не устанавливаете его перед преобразованием, а reinterpret_cast не запускается как конструктор, а просто изменяет тип указателя, полагая, что это можно сделать.
Один блок памяти переосмысливается и разделяется:
Если вы заранее знаете, что собираетесь это сделать, вы можете использовать Unions для отображения большого чака, который можно использовать позже для объединения частей памяти. Один и тот же объект будет доступен по-разному. Вы даже можете определить пространства имен и функции для него.
Также обратите внимание на BLOB-объекты, изначально используемые для возможности анализа вашей собственной необработанной памяти в данных. Перечисления (часто анонимные) часто используются в качестве политик.
Непересекающиеся блоки:
Вы можете обнаружить, что у объекта есть указатель на другой объект. Это делает другой кусок памяти, на который ссылаются и возможно доступный этим. Если вы используете союзы или разбираете BLOB-объекты или приводите исходный объект к другому объекту, вы должны быть уверены, что указатель остается на месте и действителен. Это то, что происходит в повседневной C ++, когда у класса есть члены-указатели, и мы конструируем объекты и затем указываем на них указатели. Это непересекающиеся куски памяти. Статическое и динамическое приведение к действительным классам, которые могут быть преобразованы, сохранит указатели.
Здесь есть ряд недоразумений
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*
псевдонимы. Это демонстрирует более неопределенное поведение.
Если ваш вопрос сводится к тому, можете ли вы использовать два независимых и смежных блока памяти как один блок, то нет, вы не можете.