Задать вопрос
@kruta
Студент

Что не так с работой unordered_map?

Есть unordered_map (char*, int, HashFunc, MyEqualTo, CustomAllocator>.
Я не понимаю, как он работает: при инициализации создается CustomAllocator, затем зачем-то вызывается конструктор копирования аллокатора. Буфферы копируются функцией memcpy

В итоге в каком-то аллокаторе вызывается деструктор, все данные удаляются(в т.ч. и выделенные под нужды мапы), вылетает exception в файле xmemory.

Вообще запутался. Объясните, что там такое
Дело в аллокаторе, правда не знаю, что нужно дописать

Аллокатор
#pragma once
#include <vector>


template<class T>
class CustomAllocator
{
public:

	using value_type = T;
	using pointer = T*;
	std::vector<void*>* buffers;
	void* currentBuffer;
	int bufferSize;
	size_t bufferPosition;


	CustomAllocator() noexcept
	{
		bufferSize = 1048576;
		buffers = new std::vector<void*>();
		allocateNewBuffer();
	}

	~CustomAllocator()
	{
		for (int i = 0; i < buffers->size(); ++i)
			if (buffers->at(i) != nullptr)
				delete buffers->at(i);
		delete buffers;
	}

	//CustomAllocator(CustomAllocator& other) :
	//	bufferSize(other.bufferSize), buffers(other.buffers),
	//	currentBuffer(other.currentBuffer), bufferPosition(other.bufferPosition)
	//{
	//	other.buffers = nullptr;
	//	other.currentBuffer = nullptr;
	//}

	////template<typename T1>
	////CustomAllocator(CustomAllocator<T1> other) :
	////	bufferSize(other.bufferSize), buffers(other.buffers),
	////	currentBuffer(other.currentBuffer), bufferPosition(other.bufferPosition)
	////{
	////	other.buffers = nullptr;
	////	other.currentBuffer = nullptr;
	////}


	pointer allocate(std::size_t size)
	{
		if (bufferPosition + size * sizeof(T) >= bufferSize)
			allocateNewBuffer();
		T* pointer = ((T*)currentBuffer + bufferPosition);
		bufferPosition += size * sizeof(T);
		return pointer;
	}

	void deallocate(void* p, std::size_t size)
	{
		bufferPosition -= size;
	}
private:

	void allocateNewBuffer()
	{
		currentBuffer = malloc(bufferSize);
		buffers->push_back(currentBuffer);
		bufferPosition = 0;
	}
};

UPD:
проблема была в том, что создавались копии аллокатора, которые потом уничтожались вместе с буффером, на который и выделялся под данные. В итоге шло обращение к памяти, которая была освобождена. Я так до конца и не понял, зачем нужны были копии
  • Вопрос задан
  • 523 просмотра
Подписаться 1 Средний 8 комментариев
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
Дело в том, что контейнер, или умный указатель, в современном C++ может (и делает это) несколько раз сменить аллоцируемый тип шаблонного аллокатора. При этом, оригинальный аллокатор, переданный через конструктор контейнера, будет использован для конструирования нового аллокатора с новым типом. Это означает, что новый аллокатор конструируется не через конструктор копирования, а через конструктор преобразования.

Если в шаблоне алокатора не описано правило смены аллоцируемого типа (A::template rebind<U>::other), по умолчанию при смене аллоцируемого типа будет заменен первый шаблонный параметр. Был у нас Alloc<Foo, ...>, станет Alloc<Bar, ...>.
Это означает что для правильной передачи состояния аллокатора нужно предусмотреть конструктор преобразования из аллокатора от твоего шаблона, но с другим первым аргументом.

Еще можно использовать Polymorphic Allocator, но для этого потребуется сменить стандарт на C++17.
Эту стратегию не так просто описать, поэтому я прибегну к ссылкам на доклады по этой теме.

CppCon 2017: Bob Steagall “How to Write a Custom A...
051. Modern C++ Allocators – Руслан Арутюнян (Intel)
Taming dynamic memory - An introduction to custom ...
C++Now 2018: David Sankel “C++17's std::pmr Comes ...
Это, конечно, далеко не всё на данную тему. Но цели выписывать все у меня и нет. Я привел ссылки, которым доверяю в плане чистоты информации.
Советую просто пройтись по хабру и ytube поиском докладов и статей.

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

Но если ты работаешь со стандартом до C++11, то у тебя аллокатор вообще не может иметь состояние.
All custom allocators also must be stateless. (until C++11)
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы