Declaring derived classes from base class

Jan 16, 2010 at 5:53am
Hello everyone. I am wondering if there is a way of having a program automatically declare one instance of various different objects. Basically, I have code which defines a single abstract class which has many derived classes. I would like to declare one of each derived class and put them all into a set, but I don't want to have to write out numerous lines of code to do this. What I currently have is similar to the following:

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
class Base{
    public:
        string name();
    protected:
        string Name;
        virtual bool func() = 0;
};

class Derive1 : public Base{
    protected:
        bool func();
};

class Derive2 : public Base{
    protected:
        bool func();
};

class Derive3 : public Base{
    protected:
        bool func();
};

int main()
{
    Derive1 *D1p, D1;
    D1p = &D1;

    Derive2 *D2p, D2;
    D2p = &D2;

    Derive3 *D3p, D3;
    D2p = &D3;

    set<Base*> mySet;

    mySet.insert(D1p);
    mySet.insert(D2p);
    mySet.insert(D3p);

    dosomething(mySet);

    return 0;
}


I'm wondering if there's some way that I can declare D1, D2, D3 without having to write out all these lines of code. The main reason I want to do this is that in the future I will be making more derived classes (there could end up being quite a large number of them) and want the program to adjust accordingly without having to add numerous lines to the main function. Does the class Base somehow have access to its derived types which will allow me to declare instances of them?
Jan 16, 2010 at 10:00am
Does the class Base somehow have access to its derived types which will allow me to declare instances of them?


Not automatically. But there are ways to do that.

I had to do something like this for a "smart loader" in a library I was making. The way I had it set up, I had a different class for reading different file types (like for audio there was .ogg, .mp3, etc classes, all derived from a common class). Whenever a file was loaded I needed to check all available types without limiting myself to knowing the exact classes that existed. This seems to be very similar to what you need.

What I finally settled on was creating a seperate, single, global class for each derived class. This class served as a "loader" for creating instances of the dervied class type. The loader class would add a pointer to itself into a static vector in the base class, which would allow all derived classes to be known.

It's hard to explain in words... so here's some heavily commented code:

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
//======================
// the "Loader" abstract base class
class Loader
{
public:
    virtual Base* Generate() = 0;  // generates a derived type
    
protected:
    Loader()
    {
        // the default ctor -- add this loader to the list of loaders
        Base::GetLoader().push_back(this);
    }
};

//======================
// the template for specific loaders
template <typename derivedT>
class LoaderType : public Loader
{
public:
    virtual Base* Generate()
    {
        // generate the derived type
        return new derivedT;
    }
};

//========================================
//========================================
/* The idea now is that every Derived class will have a loader, and Base
 * will have a single list of all loaders.  Thus you can interate through all possible
 * Derived types without knowing how many there are.
 */

// The base class
class Base
{
public:
    // typedef the loader list for ease
    typedef std::vector<Loader*> LoadList;
    
    // the static function to access the list
    static LoadList& GetLoader()
    {
        // NOTE:  it is very important that the load list be local and static
        //  do not make it global, because the LoaderType classes will be global
        //  and you need to avoid the "static initialization order fiasco".
        //  Same problem if the list is a static member of the class.
        // By making the LoadList local -- we assure it's constructed on first
        //  use.  Therefore ensuring the list is constructed BEFORE any
        //  objects get added to it.
        static LoadList thelist;
        return thelist;
    }
}

//======================================

// Derived classes:
class Derived : public Base
{
    // yadda yadda
    //  this class does not need anything special
};

//=======================================
//=======================================
/*  The only thing left to do is that for EACH derived class,
 * you create a single instance of a LoaderType class which
 * loads the respective Derived type.
 *
 *  Make these objects GLOBALLY in a cpp file.
 *  Or I suppose you could make them static members of the derived class.
 *
 *  For example, in Derived1.cpp:
 */
 
#include "derived1.h"
#include "otherstuff.h"

//------------------------
// the loader instance
static LoaderType<Derived1> Derived1Loader;
    // the name of the above object is unimportant.  The object need not
    // ever be used directly by your program.  The only reason
    // for its existance is for its ctor to run (adding it to the LoadList)
    // so that Derived1 types can be generated.
    
    
//-----------------------
// put your Derived1 implementation in the rest of the .cpp file

//========================================
//========================================
//========================================
/*  That's it.
 *
 *  From here, everything's ready to go.  You can now iterate through all Derived types.
 *  The catch is that Derived types will be allocated with new when generated, so
 *  you must remember to delete
 */
 
int main()
{
    set<Base*> mySet;   // a set seems kind of strange here... why not a list/deque/vector?
                        //    whatever
    
    // now populate mySet with an instance of every derived class
    for(Base::LoadList::iterator i = Base::GetLoader().begin(); i != Base::GetLoader().end(); ++i)
    {
        mySet.push_back( i->Generate() );
    }
    
    
    // -- don't forget to iterate through mySet and delete every element before clearing the set!
}


Hope this helps. You can tailor the loader and whatnot to suit your needs. And of course feel free to ask if anything seems unclear.
Last edited on Jan 16, 2010 at 10:07am
Jan 16, 2010 at 8:52pm
Thanks a lot, I'll start looking at this soon and let you know if I can get it to work out.
Jan 16, 2010 at 9:37pm
I've seen this sort of thing before. I could have sworn that the base constructor could somehow register the derived types. Could some kind of type traits or the CRTP accomplish that?
Jan 17, 2010 at 4:11am
So my program works pretty much the way I want it to now. Thanks for the help. Even though it works, I was looking for something more along the lines of what moorecm was saying rather than declaring objects globally. It just kind of seems like "cheating". Anyways, I'm just getting back into C++ after having only a brief introduction to it 6 years ago, so there are a lot of things I have to learn. If I happen to come across a nicer way of doing this, I'll let you know.

As for why I'm using a set rather than a vector, that's probably due to my background as a pure mathematician. In terms of the abstract layout of my program, it's unnecessary to use something with more structure to it (like a vector) when it is not needed, and doing so just gives me an uneasy feeling, sort of like betraying my roots. Of course, once I get better at programming again, I'll probably change this, because I would guess that for what I'm using them for, a vector would be more efficient than a set.
Jan 17, 2010 at 5:27am
I don't know of anything in the C++ RTTI system that would allow you to iterate through all known types. Let alone only specific types that are derived from a common base class.

I don't see how CRTP would help in this situation either.

If there is a simpler solution, I'd certainly be interested in hearing it. Perhaps if there were a way to enumerate types at compile time, rather than to create a list of types at runtime. But I simply don't see how that's possible in C++ (not that it isn't possible -- I'm just saying I don't see it).

Anyway I don't see this approach as "cheating" at all. Using a one-time constructed object to register itself as a type so that you can have a fully assembled list of objects before main starts seemed like the easiest to use and maintain solution to me. Yeah I know... globals... but I feel this is one of the situations where globals are a good thing.

*shrug*

As for set vs. vector.... it really depends entirely on what you're doing with the container. There are 2 major point to using a set:

1) it's ordered (faster to find a specific element if you have its key)
2) it ensures no duplicate elements.

Neither of these points seem to apply to what you're using it for. You'd have the same results as you would with a list.

But whatever.

I'm glad I could help.
Topic archived. No new replies allowed.