roymcclure
roymcclure

Reputation: 404

Assign pointer to polymorphic class in runtime

Class A contains a pointer to abstract class B (only headers implemented):

// A.h
#include "B.h"
class A {
   public:
   A();
   virtual ~A();
   pointBto(B* b_);   // { this.b = b_;  }

   private:
   B* b;

}


// B.h
class B {
    public:
    B();
    virtual ~B();
    virtual void b_method() = 0;
}

Classes C and D inherit B.

// C.h
#include "B.h"
Class C : public B {
    public:
    C();
    ~C();
    virtual b_method();

}

// D.h
#include "B.h"
Class D : public B {
    public:
    D();
    ~D();
    virtual b_method();

}

Application reads a string and depending on that, creates a new object of class C or D and points b to the created object.

Note: I don't want to create an endless chain of

if (string_ == "C")
{
   a_.pointBto(new C());
}
else if (string_ == "D")
{
   a_.pointBto(new D());
}
else ...

Upvotes: 0

Views: 67

Answers (3)

opetroch
opetroch

Reputation: 4095

If you want to avoid having an if else block (or some other similar centralized mechanism) which you extend every time you add a new derived class, you can have the derived types register themselves.

Check this thread for a trick on how to do it:

Is there a way to instantiate objects from a string holding their class name?

(note that with the solution given, the string doesn't need to be the same as the class name). Essentially you still have a map from a string to a factory function i.e.

map< std::string, std::function<B*()> >

the difference is that the code responsible for adding the callback to the map is where the derived class is defined.

Upvotes: 1

SergeyA
SergeyA

Reputation: 62563

What you are looking for is often refered to as 'virtual constructor'. I am usually not a fan of this construct, but it can be implemented using, for example, 'clone' idiom. Something like following:

struct Parent {
    virtual Parent* clone(/*??? arg*/)  = 0;
};

struct Child1 : Parent {
    /*virtual */ Parent clone(/*??? arg*/) { return new Child1(/*arg*/); }
};
/* Child 2 */

Parent* make_parent(Choice arg) {
    static std::map<Choice, Parent*> factory({{ch1, new Child1()}, {ch2, new Child2()}};
    return factory.find(arg).second->clone(/**/);
}

The bigguest problem with that is that clone arguments are aften reduced to some sort of the blob, which requires casting.

Upvotes: 1

YSC
YSC

Reputation: 40070

Simply create an std::map<std::string, std::function<B*(/*...*/)>>:

static const std::map<std::string, std::function<B*(/*...*/)>> factory = {
    { "C", [](/*...*/){ return new C(/*...*/); },
    { "D", [](/*...*/){ return new D(/*...*/); },
};

a_.pointBto(factory[string_](/*arguments*/));

Upvotes: 1

Related Questions