1

Basically, what I want to be able to do is this...

Say I have this:

class Enemy
{
public:
    Enemy();
    virtual ~Enemy();
};

class Ghost: public Enemy
{

};

class Orc: public Enemy
{

};

and I want to be able to create a Ghost or an Orc, based on some random number. Would I have to get the random number and make a switch/case to create the object I want, or is there a way to create derived class objects from the base class?

Quick note: I am just a hobby coder and have very little experience. I taught myself everything I know from books and YouTube; have mercy

Zeke Willams
  • 170
  • 11
  • 2
    I'm not sure I agree with that design choice but I don't really know enough about your other code to comment. There are other way s of creating specific types from the constructor but nothing that is much cleaner. I suppose you could have a vector of so called phantoms, which are of type Ghost or Orc in a list, and choose a random one to copy, of course this will mean implementing the copy constructor for Enemy and Orc. – George Jan 22 '17 at 13:29
  • You will have to do get the random number and then make a choice *somewhere*. You can encapsulate it in a creator method in base class. But beware, C++ polymorphisme works great with pointers of references, but read about [object slicing](http://stackoverflow.com/q/274626/3545273) before using it on objects. – Serge Ballesta Jan 22 '17 at 13:32
  • I believe there is a misconception. You don't create derived objects in the base clase, that is not possible in the way you describe it (unless you use some kind of pimpl idiom). External/client code does declare and create objects of one type. Then it can be assigned to a reference o pointer of base class type (Enemy). Can you post an example how to declare enemy and use it. In a general way. – Martin A Jan 22 '17 at 15:07
  • Thank you for the answers! The more answers I get, the more I realize how complex and vast the amount of knowledge there is for c++. – Zeke Willams Jan 22 '17 at 16:00

2 Answers2

1

You cannot use the ctor from the base class to build a subobject, because by definition the constructor only initializes fields of current class.

You can use a static factory method in the base class, but because of C++ object slicing the factory method cannot return an Enemy - I you do, you will only get a raw Enemy copying common fields from the subclass object created. A more idomatic way would be to return a pointer to a newly created object, or better a smart pointer:

class Enemy
{
public:
    Enemy();
    virtual ~Enemy();

    static std::unique_ptr<Enemy> build_enemy() {
        // get random number in choice
        Enemy * enemy;
        switch (choice) {
            case 0: enemy = new Ghost(); break;
            default: enemy = new Orc(); break;
        }
        return std::unique_ptr(enemy);
    }
};

If for any reason you prefere arrays of functions to switches, they can be used easily here:

class Enemy
{
public:
    Enemy();
    virtual ~Enemy();

    static unique_ptr<Enemy> build_enemy();
private:
    typedef Enemy* (*Builder)();
    static Enemy* buildGhost();
    static Enemy* buildOrc();
    static Builder builder[];
};

class Ghost: public Enemy
{

};

class Orc: public Enemy
{

};

Enemy* Enemy::buildGhost() {
    return new Ghost();
}
Enemy* Enemy::buildOrc() {
    return new Orc();
}

std::unique_ptr<Enemy> Enemy::build_enemy() {
    // get random choice
    Enemy *enemy = (builder[choice])();
    return std::unique_ptr<enemy>;
}

Enemy::Builder Enemy::builder[] = { Enemy::buildGhost, Enemy::buildOrc };
Community
  • 1
  • 1
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • 1
    The second is better but it still violates SOLID principles, in particular "The Open Closed Principle". I would use virtual constructor or template method pattern so that derivd objects add themselves to the array. This way Enemy class doesn't know all types of enemies. If you add a new one you have to modify the template method in the derived class and that's it. No other changes are needed in the base class. – Martin A Jan 22 '17 at 15:03
  • Thank you! This is sort of what I was thinking of. I think I just needed to think more about what exactly I wanted to do, as far as designing my code goes. Thanks to everyone who answered; I'm learning a lot! :) – Zeke Willams Jan 22 '17 at 15:57
0

you can do both, you can create a vector of pointer to base class objects std::vector<std::unique_ptr<Enemy>> v;

and then add a derived class objects to it depending on the random number

switch(number)
{
    case 1:
       v.emplace_back(new Ghost());
       break;
    case 2:
       v.emplace_back(new Orc());
       break;
}
Lorence Hernandez
  • 1,189
  • 12
  • 23