Что делает новый оператор С++ помимо распределения и вызов ctor?
Что все остальные, что делает оператор new
, кроме выделения памяти и вызова конструктора?
Ответы
Ответ 1
В стандарте С++ это говорит об одной форме объекта (обычно используемой форме) нового оператора из заголовка <new>
:
Требуемое поведение:
Возвращает ненулевой указатель на подходящую выровненную память (3.7.3) или исключение bad_alloc. Это требование является обязательным для замены версии этой функции.
Поведение по умолчанию:
- Выполняет цикл: внутри цикла функция сначала пытается выделить запрошенное хранилище. Будь то попытка связана с вызовом функции библиотеки Standard C malloc не указывается.
- Возвращает указатель на выделенное хранилище, если попытка выполнена успешно. В противном случае, если последний аргумент set_new_handler() был нулевым указателем, throw bad_alloc.
- В противном случае функция вызывает текущий new_handler (18.4.2.2). Если вызываемая функция возвращается, цикл повторы.
- Цикл завершается, когда попытка выделить запрошенное хранилище успешна или когда вызываемый Функция new_handler не возвращается.
В стандарте есть много других вещей, которые можно сказать о новом операторе и распределении динамической памяти (это ужасно много говорит), но я думаю, что список "Поведение по умолчанию" очень хорошо подводит итоги нового оператора.
Ответ 2
Я написал объяснение того, что он делает в этом ответе. Это объясняет, как
-
new
получает память
-
new
устраняет сбой памяти
-
new
обрабатывает исключения конструктора
-
new
обрабатывает специальные места размещения и версии nothrow
Майкл объяснил, как функция распределения по умолчанию (:: operator new) хорошо запоминает память и как она обрабатывает сбой. Я видел ваш вопрос о том, где размер объекта хранится в его комментариях. Ответ заключается в том, что размер не сохраняется, если не является секретным. Помните, что C не нужен размер для free
(и:: оператор new может просто использовать malloc
):
void * memory = malloc(x);
free (memory); // no need to tell it the size
Вот пример, где вы видите, как сохранение размера влияет на размер выделения для формы массива нового выражения (не охватываемого моим другим ответом):
#include <cstddef>
#include <iostream>
struct f {
// requests allocation of t bytes
void * operator new[](std::size_t t) throw() {
void *p = ::operator new[](t);
std::cout << "new p: " << p << std::endl;
std::cout << "new size: " << t << std::endl;
return p;
}
// requests deleting of t bytes starting at p
void operator delete[](void *p, std::size_t t) throw() {
std::cout << "delete p: " << p << std::endl;
std::cout << "size : " << t << std::endl;
return ::operator delete[](p);
}
};
int main() {
std::cout << "sizeof f: " << sizeof (f) << std::endl;
f * f_ = new f[1];
std::cout << "&f_ : " << f_ << std::endl;
delete[] f_;
}
Он выведет что-то вроде этого:
sizeof f: 1
new p: 0x93fe008
new size: 5
&f_ : 0x93fe00c
delete p: 0x93fe008
size : 5
Один байт для самого объекта и 4 байта для счетчика, который хранится непосредственно перед выделенной областью объекта. Теперь, если мы используем функцию освобождения без параметра размера (просто удалив его из оператора delete), мы получим этот вывод:
sizeof f: 1
new p: 0x9451008
new size: 1
&f_ : 0x9451008
delete p: 0x9451008
Среда выполнения С++ здесь не заботится о размере, поэтому она больше не хранит его. Обратите внимание, что это очень специфично для реализации и что gcc делает здесь, чтобы указать вам размер в операторе-члене delete. Другие реализации могут сохранять размер и, скорее всего, если для класса будет вызван деструктор. Например, просто добавив ~f() { }
выше, gcc сохраняет размер, независимо от того, какую функцию освобождения мы пишем.
Ответ 3
В зависимости от того, перегружен или нет, если вы создали приложение для отладки, если вы используете детектор утечки памяти, если у вас есть какая-то схема объединения памяти, если у вас есть что-то вроде сборщика мусора Boehm, который маркирует /unmarking bits и т.д. и т.д. Он может делать много пользовательских вещей внутри или вообще ничего особенного.