Is it possible to write an abstract class where the constructor chooses the appropriate subclass to instantiate in C++?

StackOverflow https://stackoverflow.com/questions/23520378

  •  17-07-2023
  •  | 
  •  

Domanda

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.

È stato utile?

Soluzione

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.

Altri suggerimenti

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top