Напыщенная речь, ИМХО, очень вводит в заблуждение, и мне кажется, что автор действительно понимает более тонкие детали, просто он, кажется, хочет ввести в заблуждение. ИМХО, ключевой момент, который показывает недостаток аргумента, следующий:
void* operator new(std::size_t size, void* ptr) throw();
Стандарт определяет, что вышеуказанная функция обладает следующими свойствами:
Возвращает : ptr.
Примечания : Преднамеренно не выполняет никаких других действий.
Для подтверждения этого - эта функция намеренно не выполняет никаких других действий . Это очень важно, так как это ключ к тому, что делает размещение new: оно используется для вызова конструктора для объекта, и это все, что он делает. Обратите внимание, что параметр size даже не упоминается.
Для тех, у кого нет времени, чтобы подвести итог: все, что «malloc» делает в C, может быть сделано в C ++ с использованием «:: operator new». Разница лишь в том, что если у вас есть неагрегированные типы, т.е. типы, для которых нужно вызывать свои деструкторы и конструкторы, тогда вам нужно вызывать эти конструкторы и деструкторы. Такие типы явно не существуют в C, и поэтому использование аргумента «malloc делает это лучше» недопустимо. Если у вас есть структура в «C», которая имеет специальную функцию «initializeMe», которая должна вызываться с соответствующим «destroyMe», то все точки, сделанные автором, в равной степени относятся к этой структуре, как и к неагрегированной структуре C ++.
Явно взяв некоторые из его пунктов:
Чтобы реализовать множественное наследование, компилятор должен фактически изменять значения указателей во время некоторых приведений. Он не может знать, какое значение вы в конечном итоге захотите при преобразовании в void * ... Таким образом, ни одна обычная функция не может выполнять роль malloc в C ++ - нет подходящего возвращаемого типа.
Это не правильно, опять же :: оператор new выполняет роль malloc :
class A1 { };
class A2 { };
class B : public A1, public A2 { };
void foo () {
void * v = ::operator new (sizeof (B));
B * b = new (v) B(); // Placement new calls the constructor for B.
delete v;
v = ::operator new (sizeof(int));
int * i = reinterpret_cast <int*> (v);
delete v'
}
Как я уже упоминал выше, нам нужно размещение new, чтобы вызвать конструктор для B. В случае 'i' мы можем привести от void * к int * без проблем. , хотя снова использование размещения new улучшит проверку типов.
Еще одно замечание, которое он делает, касается требований к выравниванию:
Память, возвращаемая новым символом [...], не обязательно будет соответствовать требованиям выравнивания struct intlist.
Стандарт под 3.7.3.1/2 гласит:
Возвращаемый указатель должен быть соответствующим образом выровнен, чтобы его можно было преобразовать в
указатель любого полного типа объекта и затем используется для доступа к объекту или массиву в выделенном хранилище (до
хранилище явно освобождается путем вызова соответствующей функции освобождения).
Это мне кажется довольно ясным.
Под специализированными распределителями автор описывает потенциальные проблемы, которые могут у вас возникнуть, например. вам нужно использовать распределитель в качестве аргумента для любых типов, которые выделяют память сами, а построенным объектам необходимо явно вызывать свои деструкторы. Опять же, как это отличается от передачи объекта-распределителя через вызов initalizeMe для структуры C?
Что касается вызова деструктора, в C ++ вы можете легко создать специальный вид интеллектуального указателя, давайте назовем его «place_pointer », который мы можем определить для явного вызова деструктора, когда он выходит из области видимости. В результате мы могли бы иметь:
template <typename T>
class placement_pointer {
// ...
~placement_pointer() {
if (*count == 0) {
m_b->~T();
}
}
// ...
T * m_b;
};
void
f ()
{
arena a;
// ...
foo *fp = new (a) foo; // must be destroyed
// ...
fp->~foo ();
placement_pointer<foo> pfp = new (a) foo; // automatically !!destructed!!
// ...
}
Последнее, что я хочу прокомментировать, это следующее:
g ++ поставляется с оператором "размещения" new [], определенным следующим образом:
inline void *
operator new[](size_t, void *place)
{
return place;
}
Как отмечалось выше, не просто так реализовано - но это требуется стандартом.
Пусть obj будет классом с деструктором. Предположим, у вас где-то есть размер байта памяти (obj [10]) и вы хотите создать 10 объектов типа obj в этом месте. (C ++ определяет sizeof (obj [10]) равным 10 * sizeof (obj).) Можете ли вы сделать это с помощью этого оператора размещения new []? Например, следующий код выглядит так:
obj *
f ()
{
void *p = special_malloc (sizeof (obj[10]));
return new (p) obj[10]; // Serious trouble...
}
К сожалению, этот код неверен. В общем, нет никакой гарантии, что аргумент size_t, переданный оператору new [], действительно соответствует размеру выделяемого массива.
Но, как он подчеркивает, предоставляя определение, аргумент размера не используется в функции распределения. Функция выделения не выполняет ничего - и поэтому единственное влияние вышеприведенного выражения размещения - вызов конструктора для 10 элементов массива, как и следовало ожидать.
Есть другие проблемы с этим кодом, но не та, что указана автором.