В процессе изучения C++ решал вот эту задачу. Если кратко, то экземпляры нешаблонного класса Any (который и нужно реализовать) должны иметь способность хранить значение любого типа, но об условии задания по ссылке, оно весьма объёмное.
Был написан следующий код (IClonable и ValueHolder здесь определены для полноты картины, в самом задании это не требуется, т.к. было реализовано раньше):
struct ICloneable
{
virtual ICloneable* clone() const = 0;
virtual ~ICloneable() { }
};
template <typename T>
struct ValueHolder : public ICloneable
{
T data_;
ValueHolder(const T& obj) : data_(obj) {}
ValueHolder* clone() const
{
return new ValueHolder(data_);
}
};
class Any
{
ICloneable* data_;
public:
Any(): data_(0) {};
~Any() { delete data_; }
template <typename T>
Any(T obj) : data_(new ValueHolder<T>(obj)) {};
Any(const Any& obj)
{
if (!obj.data_) data_ = 0;
data_ = obj.data_->clone();
}
template <typename T>
Any& operator=(const T & obj)
{
delete data_;
data_ = new ValueHolder<T>(obj);
return *this;
}
Any& operator=(const Any& obj)
{
if (this != &obj)
{
if (!obj.data_) data_ = 0;
// if (!obj.data_) return; работающая версия
delete data_;
data_ = obj.data_->clone();
}
return *this;
}
template <typename T>
T* cast()
{
if (!data_) return 0;
ValueHolder<T>* cast_try = dynamic_cast< ValueHolder<T>* >(data_);
if (!cast_try) return 0;
return &(cast_try->data_);
}
};
Тестирующая система по непонятным мне причинам указывала на ошибку сегментации.
После многочисленных танцев с бубном в конструкторе копирования я поменял if (!obj.data_) data_ = 0;
на if (!obj.data_) return;
и все по не менее непонятным мне причинам заработало.
Но дальше самое интересное, в решениях пользователей я вижу вот такую реализацию:
#include <utility>
struct ICloneable;
template <typename T>
struct ValueHolder;
struct Any {
Any() : data_(0) {}
~Any() { delete data_; }
Any(const Any& other) : data_(other.data_ ? other.data_->clone() : 0) {}
template <typename T> Any(const T& value) : data_(new ValueHolder<T>(value)) {}
template <typename T> T* cast()
{
ValueHolder<T>* castedValue;
return data_ && (castedValue = dynamic_cast< ValueHolder<T>* >(data_)) ? &(castedValue->data_) : 0;
}
Any & operator=(const Any & other)
{
if(this != &other)
{
Any tmp(other);
std::swap(data_, tmp.data_);
}
return *this;
}
private:
ICloneable* data_;
};
Можно заметить, что конструктор копирования и вообще большинство методов (кроме оператора присваивания) реализованы аналогично. Вопросы: 1. Почему не работает первая версия кода? 2. Почему вдруг работает вторая? 3. В чем разница между первой версией моего кода и кодом, который я увидел в решении и почему эта разница позволяет этому коду работать?
Закрадываются подозрения, что из-за удаления data_ в операторе присваивания могла произойти какая-то некорректная операция + возможно дело в двух версиях оператора присваивания в моем коде против одной версии в коде чужом.
Но это только смутные догадки и, даже если они верны, я был бы рад получить ответ что именно происходит и как этого избежать в будущем. (то есть преимущества реализации оператора присваивания в чужом коде в данном конкретном случае я уже оценил, но хотелось бы полного понимания процесса)
Уже заранее большое спасибо, что дочитали и пытаетесь въехать в эту проблему!
В первой версии у вас разыменовывание нулевого указателя когда obj.data_
нулевой, а во второй нет.
if (!obj.data_) data_ = 0;
data_ = obj.data_->clone();
Если бы код был лучше отформатирован, то ошибка была бы более заметна:
if(!obj.data_)
{
data_ = 0;
}
data_ = obj.data_->clone();
Вместо data_ = 0;
следовало бы написать data_ = nullptr;
, присвоение нулевого литерала указателям - это атавизм.
Ну и вообще-то стоило бы поотлаживать программу, а не гадать.
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Задали написать арканоидВозникла проблема с моментом физики поведения мяча, мяч и дощечка ( от которой он отскакивает ) представляют собой...
Не получается сделать так, чтобы при касании на экран появляется объект canvas цифра 8 в точке нажатияПомогите исправить ошибку