Доступ к дочерним членам в родительском классе, C++
я сталкиваюсь с ситуацией, когда мне нужно получить доступ к дочерним переменным-членам внутри родительского класса. Я знаю, что это противоречит принципам OO, но я должен иметь дело со сценарием, когда сотни классов наследуют от одного, и по пути половина из них перестала использовать одну из переменных родителей и объявила и использовала свои собственные (нужно было переключиться с int на int[] и, по-видимому, человек, который это сделал, не принял во внимание, чтобы применить эти изменения в родительском классе).
Один из вариантов-иметь виртуальную функцию для работы с ней, но это означает, что я должен изменить код в сотнях файлов/объектов и проверить каждый из них. Поэтому я подумал, что если можно использовать магию указателя старой школы C для получения доступа к этим переменным внутри родительского метода, это устранит необходимость сотен виртуальных функций.
В основном это то, чего я хочу достичь:
class Parent
{
void DoSomething()
{
// This is what I need
childMember = 0;
}
}
class Child1 : Parent
{
int childMember;
}
class Child2 : Parent
{
int childMember;
}
пожалуйста, дайте мне знать, если это даже возможный. Если да, то как мне этого достичь.
Другие предложения приветствуются, но имейте в виду, что я хотел бы внести изменения только в родительском классе.
ТИА.
9 ответов
единственный чистый способ-использовать подход виртуальной функции.
если Parent
класс имеет по крайней мере одну виртуальную функцию (не обязательно DoSomething
), есть также неудачный способ сделать это:
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
}
(если Parent
не имеет виртуальных функций, тогда dynamic_cast
не работает. Хотя любой класс, предназначенный для наследования, должен иметь хотя бы одну виртуальную функцию, даже если это просто деструктор.)
вероятно, CRTP помогает здесь:
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();
}
}
Я изменил код, который он компилируется-вероятно, не то, что Вы требования - но затем предоставить лучший (=компилируемый) пример кода.
Если вам разрешено изменять исходный код дочерних классов, вы можете сделать что-то вроде этого:
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;
}
};
в противном случае, если ваш объект имеет виртуальную таблицу (по крайней мере, один виртуальный метод), вы можете использовать static_cast() в сочетании с C++11 typeid, потому что это примерно в три раза быстрее, чем 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;
}
};
Я не получаю downvotes для статического приведения. Следующие работы:
#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();
}
просто не забудьте поместить объявления функций A после определения класса B. Вы можете различать дочерние классы, потому что статическое приведение будет возвращать не NULL, если вы нашли правильного ребенка. Я нахожу этот подход также полезным, потому что класс ребенка (B) почти не изменился.
по-видимому, у вас есть некоторое подмножество производных классов, которые используют childMember
.
Для этих классов у вас есть некоторый метод "DoSomething ()", который можно назвать. Я думаю, для всех других производных классов метод DoSomething()
не применимо. Почему бы вам не создать другой уровень абстракции для этого набора производных классов?
class Parent
{
// no DoSomething()
}
class ChildMemberClasses : Parent
{
int childMember;
void DoSomething()
{
// code that uses childMember
}
}
class ChildWithChildMember : ChildMemberClasses
{
// other stuff
}
если DoSomething()
имеет некоторое значение для классов без childMember, вы все равно можете определить его как виртуальный метод в Parent
. Как это:
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
}
можно использовать любопытно повторяющийся шаблон шаблон для достижения этой цели.
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>;
};
будет ли создание промежуточного абстрактного класса, содержащего childMember, к которому вам нужно получить доступ, быть опцией?
Если так:
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() { }
}
нет безопасного, надежного способа сделать это с помощью указателей. Вы можете взломать с помощью умных смещений указателя, но это будет зависеть от childMember
появляется в том же месте во всех дочерних классах.