为什么虚拟功能会破坏我的演员?

时间:2017-11-30 21:21:39

标签: c++ casting virtual downcast

我正在努力扩展以下代码:

#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;
}

此代码工作正常并打印&#39; 1&#39;那么&#39; 2&#39;如预期的那样。

但现在我想实现这样的界面:

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和虚拟析构函数来使类具有多态性。我还尝试从Base到Derived实现一个明确的向下转换构造函数,但我失败了。

PS:改变&#39; Base&#39;不是一种选择。

2 个答案:

答案 0 :(得分:2)

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;
}

答案 1 :(得分:1)

b定义为类型Base并调用运算符<<时,操作数将转换为Derived&,因此会产生未定义的行为,因为b不属于Derived类型。未定义的行为意味着一切都可能发生,包括程序按预期工作。稍微更改设置可能会产生不同的“未定义行为”,这就是您可以观察到的内容。