11

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.

kapa
  • 77,694
  • 21
  • 158
  • 175
unexplored
  • 1,414
  • 3
  • 18
  • 37

9 Answers9

9

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.)

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • The yucky way only works for one specific child class. The OP says that there are "hundreds" of child classes. – Oliver Charlesworth Aug 25 '11 at 13:19
  • 1
    @Oli: So have hundreds of `else if` branches. I did say yucky. :-) – C. K. Young Aug 25 '11 at 13:22
  • Correct me if I am wrong, but I think in this case I'll need to know exactly the name of the child that I am dealing with right? If so, then I don't have many options but implement a virtual function that forces all child classes to deal with their own attribute ( Not so much fun to do that in >200 classes :( ) – unexplored Aug 25 '11 at 13:26
  • @unexplored: Yes, you need to list all the child classes one by one. If you use the virtual function approach, have `DoSomething` call a pure virtual function. Then any child class that doesn't implement said function won't compile and/or link properly. :-) – C. K. Young Aug 25 '11 at 13:28
  • 2
    Maybe you should actually take the time to consider the design, and propose a change... over 200 classes derived from a single base class looks a bit like too many classes. – David Rodríguez - dribeas Aug 25 '11 at 14:19
  • 1
    What if childMember is protected/private? – TheNotMe Nov 16 '12 at 14:37
5

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.

Simon
  • 1,496
  • 8
  • 11
  • I think you can even use "static_cast" in ParentProxy::DoSomething(). If the member variable names are not identical in all child classes, this will also be problematic and not compile for some classes... maybe you can add some SFINAE magic and have a couple variants that cover different member names. – Daniel Feb 14 '19 at 15:43
3

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;
    }
};
Stamen Rakov
  • 456
  • 4
  • 10
2

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>;
};
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • This looks interesting, though it will still require me to change my child classes definition which I am trying to avoid. Thanks – unexplored Aug 25 '11 at 13:42
  • @unexplored Is it that you *can't* modify the derived classes or you *don't want to* because there are so many. If it's the latter, the changes above can be made using a regular expression search & replace and IMO is a much better option than having to maintain a massive `if-else if` construct like the one proposed in the accepted answer. – Praetorian Aug 25 '11 at 14:54
1

I dont get the downvotes for the static cast. The following works:

#include <stdio.h>
class B;
class A {
    public:
        A();
        void print();
    private:
        B *child;
};

class B : public A {
    friend class A;
    public:
        B();
    private:
        int value;
};

A::A(){
    child = static_cast<B*>(this);
}

void A::print(){
    printf("value = %d\n", child->value);
}

B::B(){
    value = 10;
}

int main(){
    B b;
    b.A::print();
}

Just make sure to put the declarations of the A functions after the definition of the B class. You can differentiate between child classes because the static cast will return not NULL if you have found the right child. I find this approach also beneficial because the Child class (B) is barely changed.

gospes
  • 3,819
  • 1
  • 28
  • 31
0

Apparently you have some subset of derived classes which utilise childMember. For these classes you have some method "DoSomething()" which can be called. I guess for all other derived classes the method DoSomething() is not applicable. Why don't you create another abstraction level for this set of derived classes?

class Parent
{
    // no DoSomething()
}

class ChildMemberClasses : Parent
{
    int childMember;

    void DoSomething()
    {
        // code that uses childMember
    }
}

class ChildWithChildMember : ChildMemberClasses
{
    // other stuff
}

If DoSomething() has some meaning for classes without childMember, you can still define the it as virtual method in Parent. Like this:

class Parent
{
    virtual void DoSomething()
    {
        // code that does not use childMember
    }
}

class ChildMemberClasses : Parent
{
    int childMember;

    void DoSomething()
    {
        // code that uses childMember
    }
}

class ChildWithChildMember : ChildMemberClasses
{
    // other stuff
}
fdlm
  • 11
  • 1
  • The OP was trying to avoid modifying the children classes, since there are over 200 of them. But it seems the OP doesn't have a choice any more. :-( – C. K. Young Aug 25 '11 at 13:31
  • @Chris right, but in the other case the OP has to write over 200 if/else if branches. i'd prefer refactoring in this case, since the problem won't go away in the future, and changing the parent class' code every time someone derives a class (which apparently happens often enough) is time-consuming and error-prone. – fdlm Aug 25 '11 at 13:38
  • Oh, I 100% agree (see my comment to @john about the `else if` branches being more of a counterexample). The OP agrees too. – C. K. Young Aug 25 '11 at 13:43
  • yep, didn't see that comment down there :) – fdlm Aug 25 '11 at 13:45
0

Would making an intermediate abstract class that contains the childMember you need to access be an option?

If so:

class Parent
{
    virtual void DoSomething() //Parent must be a polymorphing type to allow dyn_casting
    {
        if (AbstractChild* child = dynamic_cast<AbstractChild*>(this)) {
            child->childMember = 0;
        } else //Its not an AbstractChild
    }
}

class AbstractChild : Parent 
{ 

     int childMember; 
     virtual void DoSomething() = 0;
}

class Child1 : AbstractChild {
     virtual void DoSomething() { }
}
apokryfos
  • 38,771
  • 9
  • 70
  • 114
  • This would allow you to make classes which are derived from parent without forcing them to derive from the AbstractChild as well – apokryfos Aug 25 '11 at 13:58
-1

There is no safe, robust method of doing this with pointers. You could hack around with clever pointer offsets, but this would rely on the childMember appearing in the same location in all of the child classes.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
-1

static_cast<Child*>(this)->childMember = 0; should work.

john
  • 85,011
  • 4
  • 57
  • 81
  • And if `this` is not a `Child`, then you get UB. :-D – C. K. Young Aug 25 '11 at 13:16
  • The problem is that there are many different child classes, not just one. – Oliver Charlesworth Aug 25 '11 at 13:16
  • @Oli: Exactly. So this approach doubly won't work. (I updated my answer to describe how you can do it with `dynamic_cast`, albeit very grossly.) – C. K. Young Aug 25 '11 at 13:21
  • Well then I don't see any alternative to virtual functions. Maybe this is a good chance for a proper refactoring. My favourite programming activity. – john Aug 25 '11 at 13:22
  • @john: I agree, I'd like to think the "hundreds of `else if` branches" approach serves more as a counterexample than anything one would actually do. – C. K. Young Aug 25 '11 at 13:24
  • Re-factoring is inevitable, but not for now. I think a few comparison branches will suffice to ensure that the feature is present in the most useful parts of the program until refactoring – unexplored Aug 25 '11 at 13:34