std :: map пользовательских типов завершает работу программы при получении значения

У меня есть класс Item, у которого есть конструктор копирования, оператор присваивания, конструктор копирования и конструктор сравнения. Таким образом, его можно использовать в std :: map (правильно?).

Мне нужно использовать мой класс Item в std :: map< Предмет, Предмет>. Однако это не работает и вылетает программа, и я понятия не имею, почему.

Это мой класс предметов:

#include <iostream>
#include <string>
#include <vector>
#include <map>

enum DataType {
V_NULL,
V_INT,
V_FLOAT,
V_DOUBLE,
V_BOOL,
V_STR,
V_VECTOR,
V_MAP
};

// The Item can hold one of the types found in DataType enum
class Item {

// Everything public at first
public:
// Constructor: default is NULL
Item () {
m_type = V_NULL;
m_data = 0;
}

// Constructor:
// template, so you can specify the data in the Item
template<typename T>
Item(T actualDataInItem, DataType type) {
m_data = new Data<T>(actualDataInItem);
m_type = type;
}

// Destructor
~Item () {
delete m_data;
}

// Asignment operator:
const Item& operator= (const Item& other) {
// Check
if (this != &other) {
this->m_type = other.m_type;
// free the memory m_data points to !!!
delete m_data;
if (other.m_data != NULL)
m_data = other.m_data->clone();
}
return *this;
}

template<typename T>
const Item& operator= (const T& newData) {
delete m_data;
if (newData != NULL) {
m_data = new Data<T> (newData);
}
else {
m_data = NULL;  // just for code reading
this->m_type = V_NULL;
}

return *this;
}// Copy constructor:
Item(const Item& itemToCopy) {
this->m_type = itemToCopy.m_type;
this->m_data = itemToCopy.m_data->clone();
}

// Cast operator
template<typename T>
operator T () const {
// dynamic_cast m_data to point to "an Item of type T"Data<T>* temp_data = dynamic_cast<Data<T>* > (m_data);
return temp_data->m_dataOfAnyType;
}

// for the map
bool operator< (const Item& other) const {
return this->m_data < other.m_data;
}

// All Data inherits DataBase, so that you can
// point to any Data<>
class DataBase {
public:
// Pure virtual method for cloning the current data
// Used when assignment operator is called with an Item argument
virtual DataBase* clone () = 0;
virtual ~DataBase () { }
};

// Data is the actual information carried in an Item.
// It can be anything like a string, int, vector<Events> etc
template<typename T>
class Data : public DataBase {
public:
T m_dataOfAnyType;

// Constructors:
Data ();
Data (T data) : m_dataOfAnyType(data) { }

virtual DataBase* clone () {
return new Data<T>(*this);
}

};

// members of Item
DataType m_type;
DataBase * m_data;

};

Есть идеи?

0

Решение

Ни конструктор копирования, ни оператор присваивания для
Item верны. Конструктор копирования обращается к источнику
m_data даже когда это нулевой указатель. Кроме того, было бы
лучше использовать инициализацию, чем присваивание (хотя
это не имеет существенного значения здесь):

Item::Item( Item const& other )
: m_type( other.m_type )
, m_data( other.m_date == nullptr ? nullptr : other.m_data->clone() )
{
}

Оператор присваивания имеет несколько проблем; есть несколько
различные сценарии, которые приведут к
свисающий указатель. Наиболее очевидно, если другой указатель
ноль; Вы не устанавливаете целевой указатель на ноль. Но также, если
клонирование создает исключение, вы останетесь с непригодным для использования
объект (где даже деструктор вызовет неопределенность
поведение).

Хороший намек на то, что здесь что-то не так, это то, что вы
нужно проверить на самостоятельное назначение. Если вам нужно проверить себя
назначение, почти наверняка, что вы оператор присваивания
не правильно Самый простой способ исправить это — использовать
конструктор копирования:

Item& Item::operator=( Item const& other )
{
Item tmp;
swap( tmp );
return *this;
}

void swap( Item& other )
{
std::swap( m_type, other.m_type );
std::swap( m_data, other.m_data );
}

Тем не мение, любой решение, которое гарантирует, что все операции, которые
может произойти сбой, прежде чем любые изменения вашего объекта будут
работа, например:

Item& Item::operator=( Item const& other )
{
DataBase* new_data = other.m_data == nullptr
? nullptr
: other.m_data->clone();
delete m_data;
m_type = other.m_type;
m_data = new_data;
return *this;
}

Вы также хотите принять эту технику для других
операторы присваивания (которые аналогично нарушены).

1

Другие решения

У вас есть хотя бы одна проблема здесь

// Asignment operator:
const Item& operator= (const Item& other) {
// Check
if (this != &other) {
this->m_type = other.m_type;
// free the memory m_data points to !!!
delete m_data;
if (other.m_data != NULL)
m_data = other.m_data->clone();
}
return *this;
}

Когда вы назначаете из пункта, где m_data является null у вас есть свисающий указатель и delete будет вызван в другой раз на уничтожение или следующее назначение. Чтобы избежать этого, вы должны добавить:

delete m_data;
m_data = NULL; // Assign there so that there is no dangling pointer even if clone() throws
if (other.m_data != NULL)
{
m_data = other.m_data->clone();
}

Я не знаю, является ли это корнем вашей проблемы, но это может быть.

1

Конструктор копирования имеет ошибку

// Copy constructor:
Item(const Item& itemToCopy) {
this->m_type = itemToCopy.m_type;
this->m_data = itemToCopy.m_data->clone();
}

Вы предполагаете, что itemToCopy.m_data не NULL, но ничто не гарантирует этого, что я вижу. Это лучше

// Copy constructor:
Item(const Item& itemToCopy) {
this->m_type = itemToCopy.m_type;
this->m_data = itemToCopy.m_data ? itemToCopy.m_data->clone() : NULL;
}
0
По вопросам рекламы [email protected]