Я предполагаю, что Dictionary
имеет правильную семантику копирования.
Если вы возьмете то, что было сказано в комментариях, вот как должен выглядеть ваш код:
Сначала конструктор копирования:
template <typename K, typename V>
MyUnorderedMap<K, V>::MyUnorderedMap(const MyUnorderedMap<K, V> &source)
: m_data(new MyPair<K, V>[source.reserved_size]), // <-- You are missing this
data_size(source.data_size),
reserved_size(source.reserved_size)
{
for(int i = 0; i < reserved_size; i++)
{
m_data[i].first = source.m_data[i].first;
m_data[i].second = source.m_data[i].second;
}
}
Конструктор копирования использует список member-initialization
. Обратите внимание, что память выделена в списке инициализации элемента. Это операция, которой у вас не было в вашей версии конструктора копирования, и основная проблема с вашим кодом.
Однако есть и другие проблемы, которые можно устранить с помощью вашей реализации оператора присваивания.
Следующая функция, которую вы должны реализовать после конструктора копирования, это деструктор, а не оператор присваивания. Существует стратегическая c причина, по которой вы хотите реализовать деструктор следующим, что будет объяснено позже.
template <typename K, typename V>
MyUnorderedMap<K, V>::~MyUnorderedMap()
{
delete [] m_data;
}
Теперь, когда у вас есть эти две функции, оператор присваивания становится тривиальным. Однако давайте go над вашей ошибочной версией:
template <typename K, typename V>
MyUnorderedMap<K, V> & MyUnorderedMap<K, V>::operator=(const MyUnorderedMap<K, V>
&source)
{
if(this!=&source)
{
delete[] m_data;
data_size = source.data_size;
Давайте остановимся прямо здесь. Вы потенциально испортили свой объект этими двумя последними строками кода.
Причина в том, что вы изменили элементы вашего объекта, и вам еще предстоит выделить память. Что, если вызов new[]
в следующей строке вызовет исключение? Теперь у вас есть объект, который находится в недопустимом состоянии.
Если вы пишете оператор присваивания таким образом, он должен быть записан так, чтобы убедиться, что сначала выделена вся память, а затем начать корректировать переменные-члены объекта.
Но есть более простой способ избежать всего этого:
#include <algorithm>
//...
template <typename K, typename V>
MyUnorderedMap<K, V> & MyUnorderedMap<K, V>::operator=(const MyUnorderedMap<K, V>
&source)
{
if(this!=&source)
{
// create a temporary
MyUnorderedMap<K,V> temp(source);
// swap out the temp's contents with the current object
std::swap(temp.data, data);
std::swap(temp.data_size, data_size);
std::swap(temp.reserved_size, reserved_size);
// temp will be destroyed when the if() goes out of scope
}
return *this;
}
Так почему же это работает? Это просто, и код в основном документирует, что сделано.
Вы создаете копию переданного объекта. Затем вы заменяете содержимое текущего объекта содержимым копии. Затем копия умирает (после блока if
) со старым содержимым. Нет проблем с выдачей исключений и созданием недопустимых объектов, нет избыточного кода между конструктором копирования и оператором присваивания, et c.
Этот метод написания оператора присваивания называется идиома копирования / обмена . Единственный способ это работает, если у вас есть правильный конструктор копирования и правильный деструктор. Вот почему мы сначала написали эти две функции, чтобы воспользоваться преимуществом простого swap
-подтверждения членов в операторе присваивания.