Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access child members within parent class, C++

I am facing a situation where I need to access child member variables inside the parent class. I know this is against OO principles but I have to deal with a scenario where hundreds of classes are inheriting from one and along the way half of them stopped using one of the parents variable, and declared and used their own (there was need to switch from int to int[] and apparently the person who did that didn't take in consideration to apply this changes in the parent class instead).

One option is to have a virtual function to deal with it, but this means I have to change the code in hundreds of file/objects and test each one of them. Hence I thought if it is possible to use some old school C pointer magic to gain access to this variables inside the parent method, this will eliminate the need of hundreds of virtual functions.

Basically this is what I want to achieve:

class Parent
{
    void DoSomething()
    {
        // This is what I need
        childMember = 0;
    }
}

class Child1 : Parent
{
    int childMember;
}

class Child2 : Parent
{
    int childMember;
}

Please let me know if this is even possible. If yes how do I achieve that.
Other suggestions are welcomed, but keep in mind that I'd like to make changes only in the parent class.
TIA.

like image 347
unexplored Avatar asked Aug 25 '11 13:08

unexplored


4 Answers

You can use the curiously recurring template pattern to achieve this.

template<typename T>
class Parent
{
    void DoSomething()
    {
        // This is what I need
        T::childMember = 0;
    }

    virtual ~Parent() {}
};

class Child1 : Parent<Child1>
{
  int childMember;

  friend class Parent<Child1>;
};
like image 88
Praetorian Avatar answered Oct 22 '22 19:10

Praetorian


If you're allowed to change your child classes source code, you can do something like that:

class Parent
{
public:
    void DoSomething()
    {
        getMember() = 0;
    }
    virtual int & getMember() = 0;
};

class Child1 : public Parent
{
    int childMember;
public:
    int & getMember()
    {
        return childMember;
    }
};

class Child2 : public Parent
{
    int childMember;
public:
    int & getMember()
    {
        return childMember;
    }
};

Otherwise, if your object has virtual table (at least one virtual method), you can use static_cast() in combination with C++11 typeid, because it's about three times faster than dynamic_cast:

#include <typeinfo>

class Parent
{
public:
    virtual void DoSomething();
};

class Child1 : public Parent
{
public:
    int childMember;
};

class Child2 : public Parent
{
public:
    int childMember;
};

void Parent::DoSomething()
{
    if (typeid(Child1) == typeid(*this))
    {
        auto child = static_cast<Child1*>(this);
        child->childMember = 0;
    }
    else if (typeid(Child2) == typeid(*this))
    {
        auto child = static_cast<Child2*>(this);
        child->childMember = 0;
    }
};
like image 30
Stamen Rakov Avatar answered Oct 22 '22 18:10

Stamen Rakov


Probably CRTP helps here:

struct Parent
{
    virtual void DoSomething() = 0;
};

template <typename Derived>
struct ParentProxy : Parent
{
    virtual void DoSomething()
    {
        Derived* p = dynamic_cast<Derived*>(this);
        p->childMember = 27;
    }
};

struct Child1 : ParentProxy<Child1>
{
    int childMember;
};

struct Child2 : ParentProxy<Child2>
{
    int childMember;
};

int main()
{
    Child1 child1;
    Child2 child2;

    Parent* objects[] = { &child1, &child2 };
    const int objectCount = sizeof(objects) / sizeof(objects[0]);
    for (int index = 0; index < objectCount; ++index)
    {
        Parent* parent = objects[index];
        parent->DoSomething();
    }
}

I changed the code that it is compilable - probably not what you're requirements are - but then provide a better (=compilable) sample code.

like image 23
Simon Avatar answered Oct 22 '22 17:10

Simon


The only clean way is to use the virtual function approach.

If the Parent class has at least one virtual function (not necessarily DoSomething), there's also a yucky way to do it:

void DoSomething() {
    if (Child1* child = dynamic_cast<Child1*>(this)) {
        child->childMember = 0;
    } else if (Child2* child = dynamic_cast<Child2*>(this)) {
        child->childMember = 0;
    } // and so on, and so forth
}

(If Parent has no virtual functions, then the dynamic_cast won't work. Though, any class designed to be inherited from should have at least one virtual function, even if it's just the destructor.)

like image 9
Chris Jester-Young Avatar answered Oct 22 '22 18:10

Chris Jester-Young