动态转换为运算符中的派生类型<<

时间:2018-03-14 09:57:43

标签: c++ operator-overloading dynamic-cast

我有一个基类Tag和一个子类TagSet,它继承自Tag

class Tag
{
  public:
    Tag(std::string);
    std::string tag;
};
std::ostream & operator <<(std::ostream &os, const Tag &t);

class TagSet : public Tag
{
public:
    TagSet();
};
std::ostream & operator <<(std::ostream &os, const TagSet &ts);

及其实施

Tag::Tag(std::string t)
: tag(t)
{}

std::ostream & operator <<( std::ostream &os, const Tag &t )
{
  os << "This is a tag";
  return os;
}

TagSet::TagSet()
: Tag("SET")
{}

std::ostream & operator <<(std::ostream &os, const TagSet &ts)
{
  os << "This is a TagSet";
  return os;
}

我想要包含一个具有成员TagList的第三个类std::vector<Tag*>,该成员可以容纳Tag*个实例或TagSet*个实例。我想为<<定义TagList运算符,以便在元素为Tagoperator<<时使用Tag版本的TagSet如果元素是operator<<,则TagSet的版本。这是我的尝试:

std::ostream & operator <<(std::ostream &os, const TagList &ts)
{
  for (auto t : ts.tags)
  {
    if (t->tag == "SET")
    {
      TagSet * tset = dynamic_cast<TagSet*>(t);
      os << *tset << ", ";
    }
    else os << t->tag << ", ";
  } 
}

代码在运行时崩溃。我检查了tset指针,它不是空的。可能它是一个糟糕的演员。

这样做的正确方法是什么?这个问题与operator<<函数中的consts有关吗?关于如何实现这一点的其他建议是受欢迎的。

TagList实现的其余部分是为了完整性:

class TagList
{
public:
    TagList(std::vector<Tag*> taglist);
    std::vector<Tag*> tags;
    typedef std::vector<Tag*>::const_iterator const_iterator;
    const_iterator begin() const { return tags.begin(); }
    const_iterator end() const { return tags.end(); }
};
std::ostream & operator <<(std::ostream &os, const TagList &ts);

TagList::TagList(std::vector<Tag*> tagvec)
: tags(tagvec.begin(), tagvec.end())
{}

3 个答案:

答案 0 :(得分:5)

如果我可以针对输出Tag对象的问题提出不同的解决方案,那么Tag const&只有一个运算符重载,然后将该调用设为虚拟output Tag结构中的函数。然后在继承的类中覆盖该函数。

也许像

struct Tag
{
    ...
    virtual std::ostream& output(std::ostream& out)
    {
        return out << "This is Tag\n";
    }

    friend std::ostream& operator<<(std::ostream& out, Tag const& tag)
    {
        return tag.output(out);
    }
};

struct TagSet : Tag
{
    ...
    std::ostream& output(std::ostream& out) override
    {
        return out << "This is TagSet\n";
    }
};

然后输出列表

for (auto t : ts.tags)
    std::cout << *t;

答案 1 :(得分:1)

你不能这样做,因为operator<<不是虚拟的。

定义虚拟print方法,并在operator<<中使用,例如

class Tag {
public:
    virtual void print(std::ostream &f) const;
};

std::ostream & operator <<(std::ostream &os, const Tag &t)
{
    t->print(os);
    return os;
}

现在您可以在print()中使用方法TagList,而不需要任何演员:

std::ostream & operator <<(std::ostream &os, const TagList &ts)
{
    for (auto t : ts.tags) {
        t->print(os);
        os << ", ";
    }
}

或隐含

for (auto t : ts.tags) {
    os << *t << ", ";
}

答案 2 :(得分:0)

您当前的方法本质上是一个手写的RTTI,具有额外的内存开销。由于类TagTagSet不是多态的,因此将它与内置RTTI混合使用动态转换不会有多态性,并且这些类型的对象的内置RTTI在运行时无法执行这样的演员。如果您坚持使用手写的RTTI,那么您需要执行static_cast

TagSet & tset{static_cast<TagSet &>(*t)};