简体   繁体   中英

how to cout a subclass after static cast into a vector

I have two classes, Article and a subclass ArticleOnSale (public inheritance).

And also a class Boutique , which contains a vector<Article*> arts .

I have defined the << operator for both Article and ArticleOnSale

In Articles.cpp I have a public Addarticle

void Addarticle(Article* a)
{
    Article* p;
    if (typeid(*a) == typeid(Article))
    {
        p = new Article(a);
        arts.push_back(p);
        cout << "\narticle\n";
    }
    else
    {
        p = new ArticleEnSolde(static_cast<const ArticleEnSolde&>(*arts[arts.size()])); //creates a huge problem 
        arts.push_back(p);
    }
}

When I want to print out the articles, it only uses the << of Article for both Article and ArticleOnSale .

ostream & operator<<(ostream& n, Boutique a)
{
    n<<"\nshowing boutique:\n";
    Article* p;
    for (int i = 0; i < a.arts.size(); i++)
    {
        p = a.arts[i];
        if (typeid(*a.arts[i]) == typeid(Article))
        { 
            n << *a.arts[i];
            n << "\narticle not on sale detected\n";
        }
        else 
        {
            n << "i can detect article on sale";//just debugging (doesn't work)
        }
    }
    return n;
}

A possible implementation that takes better advantage of virtual functions would be something like this:

class Article {
public:
    virtual bool is_on_sale() const { return false; }
};

class ArticleOnSale {
public:
    virtual bool is_on_sale() const { return true; }
};

then your vector would need to look like:

std::vector<std::unique_ptr<Article>> arts;

// add an Article:
arts.push_back(std::make_unique<Article>());

// or on sale
arts.push_back(std::make_unique<ArticleOnSale>());

(the unique_ptr is needed so that the C++ virtual functions will behave, without the pointer-ness of it this wouldn't work).

Finally, the ostream operator becomes fairly concise:

ostream & operator<<(ostream& os,Boutique a ) {
    for (auto& ptr : a.arts) {
        os << (ptr->is_on_sale() ? "article on sale " : "article is full price ")
    }
}

Of course there may be better ways of going about, like maybe just storing a boolean variable inside the Article if it's on sale - but I hope that makes the code behave more like you were expecting!

There are numerous problems with this code.

In Addarticle() , typeid(*a) will always be equal to typeid(Article) . The correct way to check if a is pointing at an object of a particular derived type is to use dynamic_cast instead, eg:

if (dynamic_cast<ArticleEnSolde*>(a))
{
    // a is an ArticleEnSolde ...
}
else
{
    // a is not an ArticleEnSolde ...
}

Also, in static_cast<const ArticleEnSolde&>(*arts[arts.size()]) , accessing arts[arts.size()] is going out of bounds of the vector . You likely meant to cast a instead.

However, in this situation, you should use the clone() idiom instead, if you are not going to push a itself as-is into the vector :

class Article
{
public:
    virtual Article* clone() const;
};

class ArticleEnSolde : public Article
{
public:
    Article* clone() const override;
};

...

Article* Article::clone() const
{
    return new Article(*this);
}

Article* ArticleEnSolde::clone() const
{
    return new ArticleEnSolde(*this);
}

void Addarticle(Article* a)
{
    Article* p = a->clone();
    arts.push_back(p);
}

Regarding printing, you are invoking operator<< only for Article because that is the type of object you are passing to it. It has no way to know whether the object is actually an ArticleEnSolde or not. For polymorphic types, you should instead define a virtual method in the base class, and then define operator<< for that class and have it call the virtual method:

class Article
{
public:
    virtual void print(ostream &n) const;
};

class ArticleEnSolde : public Article
{
public:
    void print(ostream &n) const override;
};

ostream& operator<<(ostream& n, const Article &a);

...

void Article::print(ostream &n) const
{
    n << "\narticle not on sale\n";
}

void ArticleEnSolde::print(ostream &n) const
{
    n << "\narticle on sale\n";
}

ostream& operator<<(ostream& n, const Article &a)
{
    a.print(n);
    return n;
}

ostream& operator<<(ostream& n, const Boutique &a)
{
    n << "\nshowing boutique:\n";
    for (size_t i = 0; i < a.arts.size(); ++i)
    {
        n << *(a.arts[i]);
    }
    return n;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM