0

Ok hopefully this question is not duplicate as I haven't found it. As the title suggests I want to have an abstract class with, say, two subclasses. I then want to be able to create an object from this abstract class with a constructor the automatically chooses the appropriate subclass to actually create. I am not sure if this is possible, but it seems that it would be useful. For example consider the following code snippet:

#ifndef ELEMENT_H
#define ELEMENT_H

class Element
{
  public:
    int Type;
    int* nodes;
    double* xpts;
    double* ypts;
    double* zpts;

  public: Element(int typ)
  {
    Type = typ;

    if(typ==2) //linear 2D triangles
    {
      nodes = new int[3];
      xpts = new double[3];
      ypts = new double[3];
      zpts = new double[3];
    }
    else if(typ==3) //linear 2D quadrangles
    {
      nodes = new int[4];
      xpts = new double[4];
      ypts = new double[4];
      zpts = new double[4];
    }
  }
virtual int stiffnessMatrix() = 0;
virtual int massMatrix() = 0;
};



class TriLin2D : public Element
{
  public:
    double kmat[3][3];
    double mmat[3][3];
    double lvec[3];

    virtual int stiffnessMatrix();
    virtual int massMatrix();

  public: TriLin2D(int typ) : Element(typ){}
};



class SqrLin2D : public Element
{
  public:
    double kmat[4][4];
    double mmat[4][4];
    double lvec[4];

    virtual int stiffnessMatrix();
    virtual int massMatrix();

  public: SqrLin2D(int typ) : Element(typ){}
};

#endif

So this is what I have so far. If I want to create and object I would then do something like:

int Type=2;
TriLin2D e(Type); //creates a TriLin2D object

or

int Type 3;
SqrLin2D e(Type); //creates a SqrLin2D object

What I want though is to be able to do something like:

int Type=2;
Element e(Type); //automatically creates an TriLin2D object based on constructor input

or

int Type=3;
Element e(Type); //automatically creates a SqrLin2D object based on constructor input 

Hopefully you can see why this would be useful. Now I dont have to manually change the element subclasses (i.e. TriLin2D or SqrLin2D), but instead just change the Type passed to the constructor. I'm not sure if this is possible, and if it is possible, how do I do something like this? Thanks.

Note: I am new to C++ so feel free to make comments on my coding style.

James
  • 398
  • 1
  • 6
  • 19
  • 2
    Why not use the derived classes directly? – Cheers and hth. - Alf May 07 '14 at 14:21
  • 1
    You should use a factory. – Alex May 07 '14 at 14:23
  • 1
    Factory object/Factory method is the way to go. The constructor can only ever construct an object in its own class in the space it gets handed. – Deduplicator May 07 '14 at 14:23
  • No, this is not possible. The type is fixed by the constructor. However, if you wish to achieve the exact same effect use the Pimpl idiom. Put a pointer to a polymorphic implementation class in your Element class. Forward all calls to the implementation class. This is how std::function works with lambdas, for example. – kec May 07 '14 at 14:23
  • The factory pattern can do this http://en.wikipedia.org/wiki/Factory_method_pattern – sp2danny May 07 '14 at 14:24
  • @Cheersandhth.-Alf Do you mean why don't I just do TriLin2D e(2)? I can do this, but it would be nicer to have the constructor choose the appropriate subclass instead of having to manually choose it. – James May 07 '14 at 14:26
  • do you need the derived classes at all? isn't the only difference the number of points? i.e. a single `Polygon` or `Path`class? – Cheers and hth. - Alf May 07 '14 at 14:29
  • @Cheersandhth.-Alf As the above code stands right now you would probably be right, but this is a simplified version. Also I am trying to keep in line with the OOP paradigm. Both TriLin2D and SqrLin2D are different types of elements, so I think it makes sense to group them together within an abstract class called Elements. – James May 07 '14 at 14:31
  • possible duplicate of [How to implement the factory pattern in C++ correctly](http://stackoverflow.com/questions/5120768/how-to-implement-the-factory-pattern-in-c-correctly) – Deduplicator May 07 '14 at 14:35
  • @Deduplicator Thanks, I was unaware of this factory functionality which is why I didn't come across it. – James May 07 '14 at 14:39
  • @user2697246: Regarding Coding Style: Be aware that `public:` is a label which is applied to all following members. So, it does not make sense to repeat it, and it should always get its own line. Anyway, why not use `struct` if you do only public or no derivation for the class and the first (or all) members are public? Also, you might want to take a look at the `switch`-statement. – Deduplicator May 07 '14 at 14:43
  • @Deduplicator Thanks for the tips. Its funny you should mention the switch statement as I just came across that yesterday and was considering making the change. – James May 07 '14 at 15:02

2 Answers2

4

the constructor cant do this, but you can create a factory object or function that can do.

Element* CreateElement(int type){
  if(type==2)
    return new SqeLine();
  else if(type==3)
    return new TriLine();
  else
    return nullptr;
}

this also may be a static member function of your Element base class.

vlad_tepesch
  • 6,681
  • 1
  • 38
  • 80
  • Note that the disadvantage to this is that memory management can be a burden. This can be fixed by using smart pointers of course, but that exposes those to the user, which significantly changes the API. If one wishes to keep the API the same looking, the actual implementation object can be wrapped by using the Pimpl idiom, which then hides all the memory management completely. This is of course a design issue, so it just depends on what design is desired. – kec May 07 '14 at 14:30
  • @kec of course that can be hidden from user. but also then the decision has to be done in a similar way. – vlad_tepesch May 07 '14 at 14:33
  • Yes, and if you hide it from the user, you then end up essentially with the Pimpl idiom. – kec May 07 '14 at 14:39
1

A constructor's job is to turn some bit of memory into a valid object of its type.

This means that someone (the C++ runtime via incrementing the stack pointer, calling ::operator new or even you yourself when placement new is used) had to provide you with the amount of memory your class requires.

Therefore, there is no way of providing a constructor that constructs an arbitrary derived object, even if the language allowed this, since the derived object may require more memory!

What you can do however is to write a factory method, that uses new to allocate a fitting amount of dynamic memory and construct your object into it. A factory method is just an ordinary method that returns a pointer or reference to the created object, but for all intents and purposes works just like the constructor you described:

Base& factory(size_t type) {
    if(1 == type) return *static_cast<Base*>(new Derived1());
    if(2 == type) return *static_cast<Base*>(new Derived2());
    throw "You should define what happens in this case!";
}

Base& x = factory(1);
delete &x;

When dealing with a factory that creates objects with dynamic storage duration, it is often advisable to not return a reference but to return an object that ensures your object will be properly destroyed:

shared_ptr<Base> factory(size_t type) {
    if(1 == type) return shared_ptr<Base>(new Derived1());
    if(2 == type) return shared_ptr<Base>(new Derived1());
    throw "You should define what happens in this case!";
}

auto test = factory(1);

One final warning: When you only retain a pointer to a base of an object, like here, the base class should have a virtual destructor.

Community
  • 1
  • 1
danielschemmel
  • 10,885
  • 1
  • 36
  • 58
  • to hide the heap nature of the returned object and expect it to be deleted by user looks a bit awkward to me. i would suggest to directly return the pointer. (beside the fact that your factory method has controlpaths that do not return a value) – vlad_tepesch May 07 '14 at 14:37
  • @vlad_tepesch The first is an illustration how the function can look most like a constructor, while the second is a safe(-er) version as it would be created by most people. (However, I did indeed forget the `throw`.) – danielschemmel May 07 '14 at 14:42
  • yes, but in the first version you also should use a pointer not a reference since it is very uncommon that a object represented by a reference has to be manually `delete`d. – vlad_tepesch May 07 '14 at 14:46