Поведение перегруженного оператора new [] зависит от деструктора - PullRequest
0 голосов
/ 01 ноября 2018

Следующий фрагмент кода перегружает operator new[] и печатает size требуемый и адрес указателя

class MyClass
{
private:
    int _data;    //sizeof(MyClass) == 4

public:
    void* operator new[](size_t size)
    {
        cout << "MyClass::operator new[]" << endl;
        cout << "size = " << size << endl;
        void* p = malloc(size);
        cout << "p = " << p << endl;
        return p;
    }
};

int main()
{
    MyClass* a = new MyClass[100];
    cout << "a = " << a << endl;
}

выход

>>  MyClass::operator new[]
>>  size = 400
>>  p = 0x55e335a3f280
>>  a = 0x55e335a3f280

Однако, явно добавляя / определяя деструктор

class MyClass
{
...
public:
    ...
    ~MyClass() {}
};

int main()
{
    MyClass* a = new MyClass[100];
    cout << "a = " << a << endl;
}

результат изменился

>>  MyClass::operator new[]
>>  size = 408
>>  p = 0x564f30cd7280
>>  a = 0x564f30cd7288

означает, что выражение new[] запрашивает дополнительные 8 байтов памяти из operator new[]. Кажется, что дополнительные байты памяти хранят размер массива, и даже могут быть доступны!

cout << "info: " << *(reinterpret_cast<size_t*>(a) - 1) << endl;

результат

>>  info: 100

Мой вопрос: кто и почему использует эти 8 байтов информации? Это часть стандарта? Если да, есть ли объяснение, почему он так себя ведет?

Ответы [ 2 ]

0 голосов
/ 01 ноября 2018

Вы должны увидеть похожее поведение, если вместо деструктора добавите член с нетривиальным деструктором, таким как std::string.

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

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

0 голосов
/ 01 ноября 2018

не указано есть ли и сколько накладных расходов при вызове любого массива new. Такое поведение вполне соответствует снисхождению, предоставленному компилятору. Дополнительный пробел обычно используется для указания количества элементов в массиве.

При вызове delete[] должен быть вызван деструктор каждого элемента; это можно сделать, только если мы знаем, сколько там элементов. В случае, когда элементы имеют тривиальные деструкторы, ни один из них не должен вызываться, и поэтому не требуется места.

Обратите внимание, что, хотя реализации обычно делегируют std::free в operator delete, это не гарантируется, и вам также следует перегрузить operator delete.

...