У меня есть класс 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;
};
Есть идеи?
Ни конструктор копирования, ни оператор присваивания для
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;
}
Вы также хотите принять эту технику для других
операторы присваивания (которые аналогично нарушены).
У вас есть хотя бы одна проблема здесь
// 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();
}
Я не знаю, является ли это корнем вашей проблемы, но это может быть.
Конструктор копирования имеет ошибку
// 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;
}