0

I have a question on how to identify an object via a mapped pair, instantiate an object of the type identified with the pair, then store it a container of some sort (likely a vector). The hang up here is that the objects I'm looking for should all be derived classes of some base class.

Here is an example:

class BaseClass {
public:
    BaseClass() {cout << "BaseClass constructor...\n"};
    ~BaseClass() {cout << "BaseClass destructor...\n"};
};


class A : public BaseClass {
public:
    A() {cout << "A constructor...\n"};
    ~A() {cout << "A destructor...\n"};
};


class B : public BaseClass {
public:
    B() {cout << "B constructor...\n"};
    ~B() {cout << "B destructor...\n"};
};


class C : public BaseClass {
public:
    C() {cout << "C constructor...\n"};
    ~C() {cout << "C destructor...\n"};
};


int main(int argc, char *argv[])
{
    map <string, BaseClass*> my_map; // Map used to compare a string in order to identify the object type I'd like to make

    vector<BaseClass*> keyword_vct; // Vector to store the objects of the different derived class types

    BaseClass* ptr;
    my_map.insert (make_pair ("A", ptr = new A ));
    my_map.insert (make_pair ("B", ptr = new B));
    my_map.insert (make_pair ("C", ptr = new C));

    string testStr "B";

    map <string, BaseClass*> ::const_iterator it = my_map.find(testStr);
    if (it == oscards_map.end()) {
        cout << "String not found in map." << endl;
    }
    else {
        cout << it->first << "\t keyword found in map!" << endl;
        BaseClass* pSomekey;
        pSomekey = it->second; // This is where I'm lost

        keyword_vct.push_back(pSomekey); // Once I instantiate the derived object in the line above, I want to store it in a container.
    }
}   

So my main questions are:

  1. How do I make pSomekey into an object of either types A, B, or C?

  2. If I am able to instantiate one of those derived classes, am I able to store these different types of objects into the same vector because that are derived classes of BaseClass?

I notice that when I make the pairs for the map, they seem to construct an object in their respective derived classes.

I also notice that when pSomekey = it->second; is executed, no objects are constructed.

Keep in mind, that this is an example. in my real code, I'm going to be comparing hundreds of testStr to make hundreds of different objects.

Any help or advice would be greatly appreciated. Thanks!

Julian
  • 1,688
  • 1
  • 12
  • 19

3 Answers3

1
  1. Add a virtual member function in the base class to make a copy of the object.
  2. Implement them appropriately in the derived classes.
  3. Call the copy function when you need to.

Here's your code, updated at the right places.

class BaseClass
    {
    public:
        BaseClass() {cout << "BaseClass constructor...\n"};
        ~BaseClass() {cout << "BaseClass destructor...\n"};
        virtual BaseClass* clone() const = 0;
    };


class A : public BaseClass
    {
    public:
        A() {cout << "A constructor...\n"};
        ~A() {cout << "A destructor...\n"};
        virtual A* clone() const { return new A();}
    };


class B : public BaseClass
    {
    public:
        B() {cout << "B constructor...\n"};
        ~B() {cout << "B destructor...\n"};
        virtual B* clone() const { return new B();}
    };


class C : public BaseClass
    {
    public:
        C() {cout << "C constructor...\n"};
        ~C() {cout << "C destructor...\n"};
        virtual C* clone() const { return new C();}
    };


int main(int argc, char *argv[])
    {

    map <string, BaseClass*> my_map; // Map used to compare a string in order to identify the object type I'd like to make

    vector<BaseClass*> keyword_vct; // Vector to store the objects of the different derived class types

    BaseClass* ptr;
    my_map.insert (make_pair ("A", ptr = new A ));
    my_map.insert (make_pair ("B", ptr = new B));
    my_map.insert (make_pair ("C", ptr = new C));

    string testStr "B";

    map <string, BaseClass*> ::const_iterator it = my_map.find(testStr);
    if (it == oscards_map.end()) {
        cout << "String not found in map." << endl;
        } else {
        cout << it->first << "\t keyword found in map!" << endl;
        BaseClass* pSomekey;
        pSomekey = it->second;

        // Make a copy of the object and store it in keyword_vct.
        keyword_vct.push_back(pSomekey->clone());
        }

    }   
R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

Add a virtual creation function to the base class.

class BaseClass
{
    public:
        virtual BaseClass* create() const = 0;
};


class A : public BaseClass
{
    public:
       virtual BaseClass* create() const { return new A; }
};


class B : public BaseClass
{
    public:
        virtual BaseClass* create() const { return new B; }
};

// ...

keyword_vct.push_back(it->second->create()); 

The reason that pSomekey = it->second; doesn't create any objects is that you're only copying a pointer to an object, not the object itself.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
0

C++ doesn't provide a built in way refer to a "class" and create instances generically, however, you can implement it yourself.

As suggested, you can either chose to make instances have a virtual method to create similar typed objects as molbdnilo and R Sahu have suggested. This is referred to as the Prototype pattern

This has nice advantages in that you could create various objects of type "A", "B", and "C", set them up how you'd like them, and then create copies of anyone of them simply by giving them a string in your std::map and then calling the clone method whenever you wanted one.

However, There are some disadvantages.

For example, if "A" created or opened a file, what file should the dummy "A" create or open if its not actually going to be used? Cloning it would create another "A" which tried to open the same file? or create a file already created?

If "A", "B", or "C" are rather large objects, memory usage could be saved by using a slim factory class which was responsible for the construction of the more expensive object. That way memory is only used when its actually needed.

If constructing "A", "B", or "C" takes a long time, time is saved using the factory as no instances of the BaseClass need to be constructed until they're actually needed.

In general, if your derived BaseClass class constructors contain large or noticeable side effects (cpu, memory, io, etc.), using a factory can help avoid these issues.

Here's an example of how to use a Factory method pattern to solve the construction problem.

class BaseClass
{
public:
  BaseClass() { cout << "BaseClass constructor...\n"; };
  ~BaseClass() { cout << "BaseClass destructor...\n"; };
};


class A : public BaseClass
{
public:
  A() { cout << "A constructor...\n"; };
  ~A() { cout << "A destructor...\n"; };
};


class B : public BaseClass
{
public:
  B() { cout << "B constructor...\n"; };
  ~B() { cout << "B destructor...\n"; };
};


class C : public BaseClass
{
public:
  C() { cout << "C constructor...\n"; };
  ~C() { cout << "C destructor...\n"; };
};

class BaseClassFactory
{
public:
  virtual ~BaseClassFactory() {}
  virtual BaseClass *create() = 0;
};

template <class T>
class BaseClassFactoryImpl : public BaseClassFactory
{
public:
  BaseClass *create() override { return new T{}; }
};

int main(int argc, char *argv[])
{
  map <string, BaseClassFactory*> my_map; // Map used to compare a string in order to identify the object type I'd like to make

  vector<BaseClass*> keyword_vct; // Vector to store the objects of the different derived class types

  BaseClass* ptr;
  my_map.insert(make_pair("A", new BaseClassFactoryImpl<A>{}));
  my_map.insert(make_pair("B", new BaseClassFactoryImpl<B>{}));
  my_map.insert(make_pair("C", new BaseClassFactoryImpl<C>{}));

  string testStr = "B";

  map <string, BaseClassFactory*> ::const_iterator it = my_map.find(testStr);
  if (it == my_map.end()) {
    cout << "String not found in map." << endl;
  }
  else {
    cout << it->first << "\t keyword found in map!" << endl;
    BaseClass* pSomekey;
    pSomekey = it->second->create(); // Here we ask the factory to create us an instance of whatever BaseClass the factory is setup to return

    keyword_vct.push_back(pSomekey); // Once I instantiate the derived object in the line above, I want to store it in a container.
  }
}
MerickOWA
  • 7,453
  • 1
  • 35
  • 56
  • Hello, thanks for your response. My problem isn't Your a confusion between an object instance vs the object class, I understand those just fine - I must have just used the wrong lingo in my description. For the record, I'm not a professional programmer, I'm a mech engineer, so I dable in this from time to time. I'm curious, do you believe there are there advantages to using your 'factor' technique over the others mentioned above? I will likely process sometimes 100,000s of different keywords, so speed and efficiently will eventually be important to me. – Not_much_of_a _programmer Jan 15 '15 at 22:34
  • @Not_much_of_a_programmer I updated my answer to include some differences between the different approaches. – MerickOWA Jan 15 '15 at 23:44