0

For example I have the base class A and child classes B1,B2,B3 etc.

I want to write something like (or another way to do this, it's doesn't matter):

A *randomobject1;
randomobject1 = A::Getrandomobject();

And randomobject1 is pointer to object from random child class.

I don't know how to do this! Maybe I need to store references to child methods and call it after, or... I don't understand.

I need not true random, and I need to know some info before generate object.

For example the child classes contain static fields with some integers. And I need to generate object from random child class which contain this integer and it is > 30 (or with another type of field). So, classes with some integers < 30 will not take a part in generation.

  • It's it don't matter. I just want to solve the problem. I need to get random object from child classes. How to do this is not matter. – Anton Ivanov May 29 '15 at 09:06
  • Do you have a collection of child classes? Maybe a vector of pointers to the base class? Then just select one of the elements randomly. However, if you don't have your own collection of objects, then no it's not possible to find all allocated and instantiated objects. – Some programmer dude May 29 '15 at 09:06
  • What exactly are you stuck on? How to generate random numbers? How to create new objects? – nwp May 29 '15 at 09:08
  • What is the collection of child classes? I don't know how to store child classes for call after their. – Anton Ivanov May 29 '15 at 09:09
  • @nwp, I know how to generate random number. I don't know how to create object from random class. If I could store classes in something like array... – Anton Ivanov May 29 '15 at 09:13
  • you could store factories, factory methods, bruteforce if/switch it or use something like [this](http://stackoverflow.com/questions/2840640/how-to-loop-through-a-boostmpllist) – wonko realtime May 29 '15 at 09:30

3 Answers3

1

I assume that all possible subclasses are known in your getRandomObject function, and you want to create a new instance on every call. Then this is a viable solution:

A *getRandomObject() {
   int r = getRandomIntInRange(0, 3); // Some method returning a random int from [0,1,2]
   switch (r) {
   case 0: return new B1();
   case 1: return new B2();
   case 2: return new B3();
   default: return NULL; // should never come here...
}

UPDATE:
If your method might not know all possible subclasses, a registration mechanism might be possible, where you store functionoids that return new instances (factories).

A quick outline:

// Somewhere in your code
A *b1Factory() { return new B1(); }
A *b2Factory() { return new B2(); }
A *b3Factory() { return new B3(); }

// somewhere you have a factory list
typedef A* (*aSubclassFactoryFunc) (void);
std::vector<aSubclassFactoryFunc> factories;

A *getRandomObject() {
   int r = getRandomIntInRange(0, factories.size()); // Some method returning a random int from [0,1,2,...,factories.size()]
   return factories[r](); // Call random factory
}

New subclasses simply must add a factory method to the factory list.

UPDATE 2:
A registration mechanism could be done like this:

#define REGISTER_A(B) \
    struct b_subclass_register_##B {\
        b_subclass_register_##B() {\
            registerASubclass(b_subclass_register_##B::create);\
        }\
        static A *create() { return new B; }\
    } dummy_instance_##B;

This is a makro that creates a struct and creates a dummy instance in global scope. In its constructor, the subclass is registered.

You use this in your subclasses CPP files, like:

REGISTER_A(B1);
king_nak
  • 11,313
  • 33
  • 58
  • It's is good solution, but there will many child classes and if it can dynamically add new child classes, to this method, this will much better. – Anton Ivanov May 29 '15 at 09:17
0

This assumes that you know all your child classes beforehand. If not, you could create a prototype registration system with some virtual Clone function.

To create the object, I just use a random number generator and switch on the result to determine which class to construct. To handle reclaiming memory, I return a std::unique_ptr so client code doesn't need to worry about deleting the pointer.

struct A { 
    //the random child class factory method
    static std::unique_ptr<A> Getrandomobject();

    //need a virtual destructor so that the child object is deleted properly
    virtual ~A() =default;

private:
    //random number generator
    static std::mt19937 rng_;
    //some state to check if the rng has been seeded
    static bool inited_;
};
std::mt19937 A::rng_;
bool A::inited_ = false;

//our child classes
struct B1:A{};
struct B2:A{};
struct B3:A{};
struct B4:A{};

std::unique_ptr<A> A::Getrandomobject()
{
    //seed rng if this is the first call
    if (!inited_)
    {
        rng_.seed(std::random_device()());
        inited_ = true;
    }

    std::uniform_int_distribution<std::mt19937::result_type> dist(0,3);
    switch (dist(rng_))
    {
        case 0: return std::make_unique<B1>();
        case 1: return std::make_unique<B2>();
        case 2: return std::make_unique<B3>();
        case 3: return std::make_unique<B4>();
        default: return std::make_unique<B1>();
    }
}

To check that this is indeed giving us a random child class, we can use the following test code (results will be compiler dependent due to typeid):

int main() {
    auto randomobject1 = A::Getrandomobject();
    cout << typeid(*(randomobject1.get())).name();
    return 0;
}
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • This is super, but I forgot to say that I need not true random, and I need to know some info before generate object. For example the child clasess contains fields with some integers. And I need to generate object from random child class which contain this integer and it is > 30. So, classes with some integers < 30 will not take a part in generation. – Anton Ivanov May 29 '15 at 09:27
  • Will this allow @AntonIvanov to only create a random object 4 times, or no? – NathanielSantley Jul 09 '17 at 12:29
0
    #define MAX_CHILD_COUNT 3
    A* A::GetRandomObject()
    {
        int _rand_index = rand()%MAX_CHILD_COUNT; // srand() before you call rand;
        switch(_rand_index)
        {
            case 0:
                return (new B1());
            case 1:
                return (new B2());
            default:
                return (new B3());
        }
    }

as this?

IllIdan
  • 49
  • 3