C++ Polymorphic Data Structure

Viewed 449

After some searching I've yet to find an existing question which address this issue. My apologies if I missed something.

I have a base class with a params structure and setter. I would like to redefine what that params structure looks like in a derived class and set it via a base class pointer. Something like the following for example.

class A
{
public:
    struct paramsType
    {
        int a;
        int b;
    };

protected:
    paramsType params;

public:
    void setParams(const paramsType& params) { this->params = params; }
};

class B : public A
{
public:
    struct paramsType
    {
        int c;
        int d;
        int e;
    };
};

class C : public A
{
public:
    struct paramsType
    {
        int f;
    };
};

int main () {
    A* ptrB = new B();
    A* ptrC = new C();

    B::paramsType paramsB;
    paramsB.c = 0;
    paramsB.d = 1;
    paramsB.e = 2;

    C::paramsType paramsC;
    paramsC.f = 3;

    ptrB->setParams(paramsB);
    ptrC->setParams(paramsC);

    delete ptrB;
    delete ptrC;
}

I've tried making the base class "setParams" virtual and redefining it in the derived class, however when I call the method via the base class pointer, it has incompatible types.

In the real use case B and C share a ton of functionality which is implemented in A. They just have different parameter sets. I only have access to a base class pointer, but will know if the object is a B or a C. Is there an elegant way to address this?

Thanks!

2 Answers

You can't redefine a type but what you can do is a have a common base class for the parameters.

So you could make A::paramsType a base class and derive B::paramsType and C::paramsType from that. Then store a pointer to A::paramsType in class A. That way you can use a common base class for passing in the parameters.

However, in order to access members of B::paramsType and C::paramsType you'll need to cast down the inheritance hierarchy. You could implement that in a member function. If B::paramsType is derived from A::paramsType, it will be able to access all public and protected members of A::paramsType; likewise for C::paramsType.

It will be like this:

class A
{
public:
    struct paramsType
    {
        virtual ~paramsType() = default;
        int a;
        int b;
    };

protected:
    std::unique_ptr<paramsType> params;

public:
    void setParams(std::unique_ptr<paramsType> params) { this->params = std::move(params); }
};

class B : public A
{
public:
    struct paramsType : A::paramsType
    {
        int c;
        int d;
        int e;
    };

private:
    B::paramsType* getParams() { return dynamic_cast<B::paramsType*>(params.get()); }
};

A working version here.

In your example, just change A to

class A
{
public:
    struct paramsType
    {
        int a;
        int b;
        int c;
    };

protected:
    paramsType params;

public:
    void setParams(const paramsType& params) { this->params = params; }
};

Then A can use paramsType::a and b while C can just ignore paramsType::b and c.

Related