Что делает новый оператор С++ помимо распределения и вызов 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 и т.д. и т.д. Он может делать много пользовательских вещей внутри или вообще ничего особенного.