Data member for different possible derived class objects

Hi everyone,

I'm experimenting and trying to create a class that holds a shape object, where shape is an abstract base class.

For example, consider a Purse class that holds coins, each of which can be a different shape - Circle, Triangle, Square. Each coin shape is a derived class of the Coin abstract base class:

Abstract Coin
   |---> Square
   |---> Circle
   |---> Triange
   |---> etc.


Suppose the Purse can only hold 2 coins. How should I design the interface of the Purse class so that I can get and set which coin it is?

I tried having a Coin data member in the Purse class, so that I could use:
1
2
void set_coin(Coin & coin, int coin);
Coin & coin get_coin(int coin);
Where int coin indicates coin 1 or coin 2.

But what if I need a different consequent behaviour for each coin type? I then find myself wanting to use a java style instanceof setup:
1
2
3
4
if (coin instanceof Square)
{
    //do square stuff
}
But this is bad design. (edit: or is it? should I use RTTI, dynamic_cast?)

I know that I could have a virtual method in each of the derived classes, allowing me to call coin.doStuff() and then the behaviour be different for each derived class as desired. But I want to have a behaviour between the two coins in the purse, like: coin.doStuff(Coin & coin), i.e. a method where the behaviour is dependent on whether it is two circle coins, or a circle coin and a triangle coin, etc. I would want:
1
2
3
coin.doStuff(Circle circ)
coin.doStuff(Square sq)
coin.doStuff(Triange tri)
But I still have no way of getting the type of the second coin in order to pass to the first coin. I.e. I can't do this:
purse.getCoin().doStuff(purse.getCoin());



The second thing I tried was to overload the set methods:
1
2
3
void set_coin(Square square)
void set_coin(Circle circle)
void set_coin(Triangle triangle)


But then I still have no way of getting the coin back, because the following set is ambiguous:
1
2
3
Square get_coin();
Circle get_coin();
Triangle get_coin();


I really don't like my second attempt! It's horrid.


I hope someone can help! I'm sure that there is a good design solution.

Thanks,

Tom.


Last edited on
This is my solution. I am no longer using separate classes for the different shaped coins, but rather have defined a structure type for each shape. Memory conservation is important to me, so I have used a union to avoid declaring one of each structure. An integer is used to identify which shape the coin object represents.

Now my Purse (from OP) needs only to contain 2 Coin objects, and the methods that require the shapes of the coins can use the get_shape() method.

I would really appreciate feedback on this solution. Could I do better? Is dynamic_cast a better way to go? I heard that dynamic_cast comes with overheads, especially if done as part of a loop.

Would it be better to still use derived classes, but have get_shape() as an abstract function of the base class? My reasoning is that structures are better than derived classes because I only need them to be POD types.

Thanks :)

Tom.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

#include <iostream>

struct Rectangle
{
    int x1,y1;
    int x2,y2;
};

struct Circle
{
    int cx,cy;
    int rad;
};

struct Triangle
{
    int x1,y1;
    int x2,y2;
    int x3,y3;
};

class Coin 
{
private:
    int shape;
public:
    enum {CIRCLE, RECTANGLE, TRIANGLE};
    union
    {
        Rectangle rect;
        Circle circ;
        Triangle tri;
    };
    Coin(int, int, int);                  //circle
    Coin(int, int, int, int);             //rectangle
    Coin(int, int, int, int, int, int);   //triangle
    virtual ~Coin() {};
    int get_shape() {return shape;}
};

//constructor: circle
Coin::Coin(int centre_x, int centre_y, int radius) {
    shape = CIRCLE;
    circ = Circle {centre_x, centre_y, radius};
}

//constructor: rectangle
Coin::Coin(int x_1, int y_1, int x_2, int y_2) {
    shape = RECTANGLE;
    rect = Rectangle {x_1, y_1, x_2, y_2};
}

//constructor: triangle
Coin::Coin(int x_1, int y_1, int x_2, int y_2, int x_3, int y_3) {
    shape = TRIANGLE;
    tri = Triangle {x_1, y_1, x_2, y_2, x_3, y_3};
}

int main()
{
    using std::cout;
    using std::endl;
    
    Coin coin(5,8,10,11);
    
    if (coin.get_shape() == coin.CIRCLE)
    {
        cout << "I am a circle. I have a centre located at (" << coin.circ.cx << ", " << coin.circ.cy;
        cout << ") and a radius of " << coin.circ.rad << "." << endl;
    }
    else if (coin.get_shape() == coin.RECTANGLE)
    {
        cout << "I am a rectangle. I have corner locations: (" << coin.rect.x1 << ", " << coin.rect.y1;
        cout << ") and (" << coin.rect.x2 << ", " << coin.rect.y2 << ")." << endl;
    }
    else if (coin.get_shape() == coin.TRIANGLE)
    {
        cout << "I am a triangle. I have vertices: (" << coin.tri.x1 << ", " << coin.tri.y1 << "), (";
        cout << coin.tri.x2 << ", " << coin.tri.y2 << "), and " << coin.tri.x3 << ", " << coin.tri.y3 << ")." << endl;
    }
    else
        cout << "Could not determine the type state of the region object." << endl;
    
    return 0;
}

It's not bad. There are some things that should be taken into account though.

First, your union's size is six ints, no matter what the type of your coin is.
Second, virtual functions exist precisely to eliminate if statement cascades
like the one in your main function.

Personally, I would stick to virtual functions for single dispatch.
Double (and, in general, multiple) dispatch is still a problem
though, as C++ doesn't directly provide something about this.

This could be of help -> http://cplusplus.com/forum/general/25998/#msg138353

This could also be of help:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <iostream>
#include <utility>
#include <map>
using namespace std;

class Shape
{
    class Dispatcher
    {
        struct BaseFunction
        {
            virtual ~BaseFunction() {}
            virtual void operator()(Shape & s1, Shape & s2) {}
        };

        template <class T1, class T2>
        struct Function : public BaseFunction
        {
            typedef void (*Callback)(T1 &,T2 &);
            Callback f;

            Function(Callback f_):f(f_) {}
            virtual void operator()(Shape & s1, Shape & s2) { f((T1&)s1,(T2&)s2); }
        };

        typedef pair<void*, void*> Key;
        typedef map<Key, BaseFunction*> DispatchMap;

        DispatchMap dispatch_map;
        bool symmetric; // is f(s1,s2)==f(s2,s1) ?

    public:

        Dispatcher(bool s):symmetric(s) {}
        ~Dispatcher()
        {
            DispatchMap::iterator it=dispatch_map.begin();
            while (it!=dispatch_map.end()) {delete it->second; it++;}
        }

        template <class T1, class T2>
        void Add(typename Function<T1,T2>::Callback f)
        {
            DispatchMap::iterator it;
            Key type(T1::get_type_static(),T2::get_type_static());

            it=dispatch_map.find(type);
            if (it!=dispatch_map.end()) delete it->second;

            dispatch_map[type]=new Function<T1,T2>(f);
        }

        void Call(Shape & s1, Shape & s2, bool check_symmetric=true)
        {
            DispatchMap::iterator it;
            Key type(s1.get_type(),s2.get_type());

            it=dispatch_map.find(type);
            if (it==dispatch_map.end())
            {
                if (symmetric && check_symmetric)
                    return Call(s2,s1,false);
                else return;
            }

            (*it->second)(s1,s2);
        }
    };

public:

    virtual ~Shape() {}
    virtual void * get_type() { return 0; }

    static Dispatcher dispatcher;
};

Shape::Dispatcher Shape::dispatcher(true);

class Circle : public Shape
{
public:

    static void * get_type_static() {static int type=0; return &type;}
    virtual void * get_type() {return get_type_static();}
};

class Rectangle : public Shape
{
public:

    static void * get_type_static() {static int type=0; return &type;}
    virtual void * get_type() {return get_type_static();}
};

class Triangle : public Shape
{
public:

    static void * get_type_static() {static int type=0; return &type;}
    virtual void * get_type() {return get_type_static();}
};

void fun_cc(Circle & s1, Circle & s2) {cout << "circle-circle" << endl;}
void fun_cr(Circle & s1, Rectangle & s2) {cout << "circle-rectangle" << endl;}
void fun_tr(Triangle & s1, Rectangle & s2) {cout << "triangle-rectangle" << endl;}

int main()
{
    Shape::dispatcher.Add<Circle,Circle>(fun_cc);
    Shape::dispatcher.Add<Circle,Rectangle>(fun_cr);
    Shape::dispatcher.Add<Triangle,Rectangle>(fun_tr);

    Circle c;
    Rectangle r;
    Triangle t;

    Shape & sc=c;
    Shape & sr=r;
    Shape & st=t;

    Shape::dispatcher.Call(sc,sc); // C-C
    Shape::dispatcher.Call(sc,sr); // C-R
    Shape::dispatcher.Call(sr,sc); // C-R (symmetry)
    Shape::dispatcher.Call(st,sr); // T-R
    Shape::dispatcher.Call(sr,st); // T-R (symmetry)
    Shape::dispatcher.Call(st,st); // T-T doesn't exist

    cout << "\nhit enter to quit...";
    cin.get();
    return 0;
}

Note that the Dispatcher class is not tightly tied to the Shape inheritance tree.
You can easily modify it to be a generic mechanism that can be used for double dispatch in any class hierarchy.
Last edited on
That's really cool! I love it :-D

Thank you!

I'll see if I can explain how it works, and maybe you could tell me where I'm wrong/not-quite-right?

The Dispatcher class is used to map pairs of shape types with the desired corresponding function, and use this map to call these functions when required. In Dispatcher class:

(*) BaseFunction structure and the derived Function structure template encapsulate a response function to a particular pair of Shapes. Constructor specified in the Function structure template takes a Callback type, which has just been defined as typedef void (*Callback)(T1 &,T2 &); or "a pointer to a function returning void and taking parameters T1&, and T2&, determined upon template specialisation".

(*) An STL map container is used with pair keys to map shape-type combinations to the desired function.

(*) A boolean 'symmetric' is used to indicate whether or not if (s1,s2) cannot be found in the map, we should search for (s2,s1) instead and call its function: typedef map<Key, BaseFunction*> DispatchMap. This is the purpose of the Function template inheriting from the BaseFunction structure, otherwise the typedef of DispatchMap would need to be a template too, to keep Function<> generic.

(*) Add function template. Takes a Function structure as a parameter. The Add function adds the Function structure to the private member DispatchMap, with a pair containing the two shape types as the key (if the key is found within the map already, then the new key and value overwrite the old; this is not a multimap).

(*) Call function. Takes two Shapes and a boolean that allows the caller to specify whether the key (s2,s1) should be tried if (s1,s2) is not found. If the dispatcher has its symmetric boolean == false, then this flag is ignored and (s2,s1) is never tried. The Call function searches for the key (types of the two parameter shapes) in the private DispatchMap instance, if found, the corresponding function is called.

-------------------------------------------------------------------------------------------

What I don't understand are the get_type() and get_type_static() methods. Why set static int type=0; and what is &type referring to?

Thanks again for your help! I love this solution :-D

Tom.

-------------------------------------------------------------------------------------------


For anyone else who reads this some time in the future - here are a few references I found handy while going through the code:

Void pointers (e.g. void*) ==> http://theoryx5.uwinnipeg.ca/programming/node87.html

Function pointer declarations with a typedef (e.g. typedef void (*Callback)(T1 &,T2 &);) ==> http://www.devx.com/tips/Tip/13829 - Function Pointer Declarations With a typedef

Also, in the following:
1
2
3
4
5
6
7
8
9
it=dispatch_map.find(type);
if (it==dispatch_map.end())
{
    if (symmetric && check_symmetric)
        return Call(s2,s1,false);
    else return;
}

(*it->second)(s1,s2);


it is an iterator for the STL map container. After it = dispatch_map.find(type), if it == dispatch_map.end(), then this means that find() reached the end of the map without finding type as a key. In this case, if the functions are symmetric, we can try searching for (s2,s1) in the map instead of (s1,s2). Otherwise, return because there are no other options.

If this is not the case, the required function was found, and it->second points to it. Thus (*it->second) is like using the function name, and (*it->second)(s1,s2); is like calling the function.
That's some interesting code, took me a while to understand it. Once you've learned the language I find it's not easy to learn more advanced techniques on the internet, anybody care to point me in the direction of some really advanced c++ tutorials? I can't seem to find much
It's not a tutorial, but I really like the book C++ Primer by Stephen Prata. While it is an intro, you can mostly skim through the beginner stuff, and it still provides a concise, in depth understanding of the features, and why they are how they are. All of the features used in the above code are covered by the book. It also has a really good list of recommended advanced reading at the back.
thomas430 wrote:
I'll see if I can explain how it works, and maybe you could tell me where I'm wrong/not-quite-right?

You pretty much got it right.
It makes me wonder if you're a professional programmer pretending to be a newbie...

thomas430 wrote:
What I don't understand are the get_type() and get_type_static() methods. Why set static int type=0; and what is &type referring to?

I need a way to represent the type of each Shape. I use the address of a unique per class variable to do this. Let's consider a friendlier to the eye approach of what I did:

1
2
3
4
5
6
7
8
9
10
11
class Circle : public Shape
{
public:

    static int type;

    static void * get_type_static() {return &type;}
    virtual void * get_type() {return &type;}
};

int Circle::type;

Here, type is a unique variable for the Circle class. All Circle objects share it.
Similarly, all Rectangle objects share Rectangle::type and all Triangle objects share Triangle::type.

It's obvious that there's a 1-1 mapping between the type variables and the concrete shape classes.
Also, as the type variables (obviously) have different addresses, it follows that there's a 1-1 mapping
between the addresses of the type variables (&type) and the concrete shape classes,
and that's exactly what I use here.

Initializing the type variables to zero is not really necessary, as I never use their values in any way.

thomas430 wrote:
I really like the book C++ Primer by Stephen Prata.

Stephen Prata's C++ Primer Plus is the book I learned C++ from.

quirkyusername wrote:
anybody care to point me in the direction of some really advanced c++ tutorials?

Check out Modern C++ Design by Andrei Alexandrescu.
Google "design patterns c++". You'll most probably find several good links.
C++ related articles at http://www.codeproject.com/ could help too.
Last edited on
I see, that's clever.

It makes me wonder if you're a professional programmer pretending to be a newbie...

Haha, I'm an engineering student migrating from Java to C++, so I'm a half-newbie :) Prata made things much easier!
Topic archived. No new replies allowed.