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