Почему виртуальная функция нарушает мой кастинг?

Я борюсь с расширением следующего кода:

#include <iostream>

class XmlTree {};

class Base
{
protected:
int var;
public:
Base(int var) : var(var) {}
virtual ~Base() {}
};

class Derived : public Base
{
public:
void SerializeTo(XmlTree& tree) const { std::cout << var << std::endl; }
void DeserializeFrom(const XmlTree& tree) { var = 2; }
};

void operator<<(XmlTree& tree, const Base& b) { static_cast<const Derived&>(b).SerializeTo(tree); }
void operator>>(const XmlTree& tree, Base& b) { static_cast<Derived&>(b).DeserializeFrom(tree); }

int main() {
Base b(1);
XmlTree tree;
tree << b;
tree >> b;
tree << b;
}

Этот код работает нормально и печатает «1», а затем «2», как и ожидалось.

Но теперь я хотел бы реализовать интерфейс так:

class XmlInterface
{
public:
virtual void SerializeTo(XmlTree& tree) const = 0;
virtual void DeserializeFrom(const XmlTree& tree) = 0;
};

class Derived : public Base, public XmlInterface
{
public:
virtual void SerializeTo(XmlTree& tree) const override { std::cout << var << std::endl; }
virtual void DeserializeFrom(const XmlTree& tree) override { var = 2; }
};

TL; DR: Как я могу заставить это работать?

Я попытался использовать dynamic_cast и виртуальные деструкторы, чтобы сделать классы полиморфными. Я также пытался реализовать явный конструктор downcast из Base в Derived, но с треском провалился.

PS: Изменение «База» не вариант.

0

Решение

b это не Derived объект, поэтому приведение его к Derived является неопределенное поведение.

В первом примере правильное решение — переместить методы сериализации в Base и сделать их виртуальными / абстрактными так Derived может переопределить их. Затем создайте Derived возразите и снимите приведения с ваших операторов:

#include <iostream>

class XmlTree {};

class Base
{
protected:
int var;
public:
Base(int var) : var(var) {}
virtual ~Base() {}
virtual void SerializeTo(XmlTree& tree) const = 0;
virtual void DeserializeFrom(const XmlTree& tree) = 0;
};

class Derived : public Base
{
public:
Derived(int var) : Base(var) {}
void SerializeTo(XmlTree& tree) const override { std::cout << var << std::endl; }
void DeserializeFrom(const XmlTree& tree) override { var = 2; }
};

void operator<<(XmlTree& tree, const Base& b) { b.SerializeTo(tree); }
void operator>>(const XmlTree& tree, Base& b) { b.DeserializeFrom(tree); }

int main() {
Derived d(1);
XmlTree tree;
tree << d;
tree >> d;
tree << d;
}

Сделайте что-то подобное во втором примере:

#include <iostream>

class XmlTree {};

class Base
{
protected:
int var;
public:
Base(int var) : var(var) {}
virtual ~Base() {}
};

class XmlInterface
{
public:
virtual void SerializeTo(XmlTree& tree) const = 0;
virtual void DeserializeFrom(const XmlTree& tree) = 0;
};

class Derived : public Base, public XmlInterface
{
public:
Derived(int var) : Base(var) {}
void SerializeTo(XmlTree& tree) const override { std::cout << var << std::endl; }
void DeserializeFrom(const XmlTree& tree) override { var = 2; }
};

void operator<<(XmlTree& tree, const XmlInterface& intf) { intf.SerializeTo(tree); }
void operator>>(const XmlTree& tree, XmlInterface& intf) { intf.DeserializeFrom(tree); }

int main() {
Derived d(1);
XmlTree tree;
tree << d;
tree >> d;
tree << d;
}
2

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

При определении b как тип Base и вызывающий оператор <<где операнд приведен к Derived&, вы получаете неопределенное поведение, потому что b не тип Derived, Неопределенное поведение означает, что все может произойти, включая программу, работающую так, как задумано. Небольшое изменение настроек может привести к другому «неопределенному поведению», и это то, что вы можете наблюдать.

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector