• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 251
  • Last Modified:

polymorphism?

Hi,

I have a few classes all derived from a base. A container class keeps some vectors of these container classes. Right now I have an Add() method for each class type to the container - but it's just the same code repeated, with a different class type of course. Is there a way I can have one Add() method and I can throw any derived type at it? I don't want to have 20  add methods for each class type:

class BASE {

};

class Apple : public BASE {
    bool operator==(const Apple& a) const {
        return ...;
    }
};

class Orange : public BASE {
    bool operator==(const Orange& o) const {
        return ...;
    }
};

class Container {

    vector<Apple> m_vApples;
    vector<Orange> m_vOranges;

    void AddApple(const Apple& apple)
    {
        if (find(m_vApples.begin(), m_vApples.end(), apple) != m_vApples.end()) {
            throw ("already exists");
        }
        m_vApples.push_back(apple);
    }

    void AddOrange(const Orange& orange)
    {
        if (find(m_vOranges.begin(), m_vOranges.end(), orange) != m_vOranges.end()) {
            throw ("already exists");
        }
        m_vOranges.push_back(orange);
    }
};

Yeah and I will have like 20 different class types in the container class - so I don't want to have 20 Add?? methods.

Thanks
0
DJ_AM_Juicebox
Asked:
DJ_AM_Juicebox
1 Solution
 
MikeGeigCommented:
Hello,

Sounds to me like what you need is templating. Templating essentially allows you to take one function, and write it to cover many variable types.

Here is a usefull guide
http://www.is.pku.edu.cn/~qzy/cpp/vc-stl/templates.htm

Basically, your functions are written generically, so that the compiler can choose which types to use at run-time.
0
 
itsmeandnobodyelseCommented:
You could do:

    void Container::AddFruit(const Base& fruit)
    {
        switch(fruit.fruitId())
        {
        case APPLES:
            if (find(m_vApples.begin(), m_vApples.end(), dynamic_cast<const Apple&> (fruit)) != m_vApples.end()) {
                throw ("already exists");
            }
            m_vApples.push_back(dynamic_cast<const Apple&>  (fruit);
            break;
        case ORANGES:
            if (find(m_vOranges.begin(), m_vOranges.end(), dynamic_cast<const Orange&> (fruit)) != m_vOranges.end()) {
                throw ("already exists");
            }
            m_vOranges.push_back(dynamic_cast<const Orange&>  (fruit));
            break;
        }
        ...
    }

but actually it is worse than 20 functions.

The problem isn't your add function but the 20 different vectors which each take the derived class by value. Because of that you can't make it generic (beside you would make some real dirty casts).

You could store Base* pointers for each derived class. The addFruit function would  take Base& and make a copy of the given object by means of a virtual function.

typedef bool (*CompareFruit)(const Base* pf1, const Base* pf2);

class Container {

    enum Fruits { APPLE, ORANGE, ..., MAX_FRUIT };

    vector<Base*> m_vFruits[MAX_FRUIT];
    CompareFruit  m_vCompFunc[MAX_FRUIT];


    void AddFruit(const Base& fruit)
    {
        vector<Base*>& current = m_vFruits[fruit.fruitId()];

        if (find(current.begin(), current.end(), &fruit, m_vCompFunc[fruit.fruitId()]) != current.end()) {
                throw ("already exists");
            }
        current.push_back(fruit.makeCopy());
    }
};

Hope, it's understandable.

Regards, Alex

0
 
itsmeandnobodyelseCommented:
In the above sample each derived class needs to implement a compare function which can be added to the array of compare functions. The compare functions are necessary if storing pointers cause find would compare pointers instead of values if not doing so. If the id of each 'frruit' is a baseclass member you could do all with a compare function provided by Base or Container.

If you want the array of vectors to be a dynamic vector you need to use a map rather than a fixed-sized array. Each derived class would register at the containg passing its class name and the compare function (and copy function if you want). Then addFruit would retrieve the classname via virtual function from fruit, and use it as a key to the static map where all derived classes have registered. The value of the map is a struct that contains the vector, the compare function (and the copy function),

Regards, Alex
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
jkrCommented:
I'd use a virtual base interface for that, e.g.

class BASE {

public:

      enum Type {

            tyApple,
            tyOrange
      };

      virtual const Type GetType () const = 0;

};

class Apple : public BASE {

      public:

      virtual const Type GetType () const { return tyApple;}

    bool operator==(const Apple& a) const {
        return tyApple == a.GetType() && AllMembersMatch();
    }
};

class Orange : public BASE {

    public:

      virtual const Type GetType () const { return tyOrange;}

      bool operator==(const Orange& o) const {
        return tyOrange == o.GetType() && AllMembersMatch();
    }
};

class Container {

    vector<BASE&> m_vElements;

    void AddElement(const BASE& elem)
    {
        if (find(m_vElements.begin(), m_vElements.end(), elem) != m_vElements.end()) {
            throw ("already exists");
        }
        m_vElements.push_back(elem);
    }

};
0
 
itsmeandnobodyelseCommented:
>>>> Sounds to me like what you need is templating
Yes, Mike is right. A template would solve that you only have to provide one add function. However as template functions cannot be made member functions you either need a global template function thus moving the problem of instantiating each template to the caller *or* you provide a member function which takes a const Base& and have to call each template using a switch like I did above.

void Container::add(const Base& fruit)
{
     switch(fruit.getFruitId())
     {
     case APPLE:      addFruit(dynamic_cast<const Apple&> (fruit)); break;
     case ORANGE:  addFruit(dynamic_cast<const Orange&> (fruit)); break;
     ....
     }
}

Though it is a one-liner for each case it isn't very pretty.

Note, you may pass fruit without cast and make the dynamic_cast in the template. However, you would have to add the template type then:

     case APPLE:      addFruit<const Apple&> (fruit); break;

Regards, Alex
0
 
itsmeandnobodyelseCommented:
>>>> vector<BASE&> m_vElements;

A vector can't store references. You would need to use pointers as I showed above.


0
 
itsmeandnobodyelseCommented:
>>>> vector<BASE*> m_vElements;

Moreover, all fruits are in one vector what is different to the spec....
0
 
DarrylshCommented:
Ok here is a templated version and a vector of base version

#include <string>
#include <vector>
#include <algorithm>


class BASE
{
public:
      std::string m_name;
      int m_id;

      bool operator==(const BASE B) const
      {
        return B.m_name == m_name && B.m_id == m_id;
      }
};

class Apple : public BASE
{
public:
      Apple(int id)
      {
            m_name="apple";
            m_id=id;
      }

};

class Orange : public BASE
{
public:
      Orange(int id)
      {
            m_name="orange";
            m_id=id;
      }
};

template <typename T>
class Container // can hold fruit defined in template
{      
      std::vector<const T> m_vFruit;

public:    
    void AddFruit(const T& fruit)
    {
        if (find(m_vFruit.begin(), m_vFruit.end(), fruit) != m_vFruit.end())
            {
            throw ("already exists");
        }
        m_vFruit.push_back(fruit);
    }
};

class Container2 // can hold any fruit derived from base
{
      std::vector<const BASE> m_vFruit;
public:
      void AddFruit(const BASE& fruit)
      {
            if (std::find(m_vFruit.begin(), m_vFruit.end(), fruit) != m_vFruit.end())
            {
            throw ("already exists");
        }
        m_vFruit.push_back(fruit);
    }
};



      
int main()
{
      Container<Apple> apple_container;
      Container<Orange> orange_container;
      Container<BASE> templated_fruit_container;

      Container2 fruit_container;

      Apple apple(1);
      Orange orange(1);
      


      apple_container.AddFruit(apple);
      orange_container.AddFruit(orange);

      templated_fruit_container.AddFruit(apple);
      templated_fruit_container.AddFruit(orange);
      
      fruit_container.AddFruit(apple);
      fruit_container.AddFruit(orange);
}
0
 
jkrCommented:
>>A vector can't store references. You would need to use pointers

Yes, that's right, but not too much of a difference, since the emphasis was put on the interface

>>Moreover, all fruits are in one vector

Yes, that was the idea.
0
 
itsmeandnobodyelseCommented:
>>>> but not too much of a difference
One difference is that you have to make copies if not taking the *pointer* given what normally is a bad doing for a container.  Another difference is that you can't check for duplicates without providing a compare function for each pointer type.

The last argument may spoil the idea of putting all fruits to one container as it is difficult to compare apples with oranges, isn't it?

0
 
itsmeandnobodyelseCommented:
>>>> int main()
>>>> {
>>>>      Container<Apple> apple_container;
>>>>      Container<Orange> orange_container;

No, obviously you will not define the containers at function level if you already invested so much in polymophism...

So, you would need a

class FruitContainer
{
    Container<Apple> apple_container;
    Container<Orange> orange_container;
    ...
};

and we are at the begin again ....

Regards, Alex
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now