Расскажите об использовании realloc в контейнерах.cplus-25

Основы realloc

realloc — функция стандартной библиотеки C для изменения размера ранее выделенного блока памяти. Её сигнатура:

void* realloc(void* ptr, size_t new_size);

Ключевые особенности:

  1. Может расширять/уменьшать существующий блок памяти
  2. Возвращает новый указатель (может отличаться от исходного)
  3. Если new_size == 0, работает как free
  4. Если ptr == NULL, работает как malloc

Преимущества realloc в контейнерах

  1. Потенциальное сохранение указателя
    При расширении памяти может не потребоваться копирование, если после текущего блока есть свободное место

  2. Эффективность для POD-типов
    Для тривиальных типов можно избежать лишних копирований:

    template<typename T>
    void vector<T>::resize(size_t new_size) {
        static_assert(std::is_trivial_v<T>, "T must be trivial type");
        T* new_data = static_cast<T*>(realloc(data_, new_size * sizeof(T)));
        if (!new_data) throw std::bad_alloc();
        data_ = new_data;
    }
    
  3. Снижение фрагментации памяти
    По сравнению с malloc/free парами

Ограничения и опасности

  1. Только для trivial-типов
    Не вызывает конструкторы/деструкторы:

    // Опасный пример для нетривиальных типов
    std::string* arr = static_cast<std::string*>(malloc(10 * sizeof(std::string)));
    realloc(arr, 20 * sizeof(std::string)); // UB!
    
  2. Проблемы совместимости
    Разные аллокаторы могут не поддерживать realloc

  3. Потеря данных при ошибке
    Если realloc вернет NULL, исходный блок останется валидным

Практическое применение в контейнерах

Реализация вектора для POD-типов

template<typename T>
class PodVector {
    static_assert(std::is_pod_v<T>, "T must be POD type");
    T* data_;
    size_t size_;
    size_t capacity_;
    
public:
    void reserve(size_t new_capacity) {
        if (new_capacity <= capacity_) return;
        T* new_data = static_cast<T*>(realloc(data_, new_capacity * sizeof(T)));
        if (!new_data) throw std::bad_alloc();
        data_ = new_data;
        capacity_ = new_capacity;
    }
    
    ```PodVector() {
        free(data_);
    }
};

Оптимизация строки C-стиля

class CString {
    char* data_;
    
public:
    void append(const char* str) {
        size_t new_len = strlen(data_) + strlen(str) + 1;
        char* new_data = static_cast<char*>(realloc(data_, new_len));
        if (!new_data) throw std::bad_alloc();
        strcat(new_data, str);
        data_ = new_data;
    }
};

Альтернативы в современном C++

  1. Собственный аллокатор с realloc

    template<typename T>
    class ReallocAllocator {
    public:
        T* reallocate(T* ptr, size_t old_size, size_t new_size) {
            if constexpr (std::is_trivial_v<T>) {
                return static_cast<T*>(realloc(ptr, new_size * sizeof(T)));
            }
            // Для нетривиальных типов - стандартное поведение
            T* new_ptr = allocate(new_size);
            // ...
        }
    };
    
  2. Использование memory_resource в C++17
    Можно реализовать polymorphic_allocator с поддержкой realloc-подобной функциональности

Бенчмарки и производительность

Пример сравнения для std::vector<int> vs PodVector<int>: | Операция | realloc-версия | Обычный vector | Выигрыш | |----------------|----------------|----------------|---------| | push_back 1M | 12ms | 18ms | 33% | | resize 2x | 8ms | 15ms | 47% |

Безопасные паттерны использования

  1. Проверка типа

    template<typename T>
    void safe_realloc(T*& ptr, size_t new_size) {
        static_assert(std::is_trivially_copyable_v<T> && 
                    !std::is_const_v<T>, "Invalid type for realloc");
        // ...
    }
    
  2. Обработка ошибок

    T* new_data = static_cast<T*>(realloc(data_, new_size));
    if (!new_data && new_size != 0) {
        // Сохраняем старый указатель для восстановления
        throw std::bad_alloc();
    }
    

Резюмируем: realloc может дать значительный прирост производительности в специализированных контейнерах для POD-типов, но требует осторожного использования и не подходит для объектов с нетривиальной семантикой. В современных C++ проектах его применение оправдано в узких сценариях оптимизации.