Доступ к дочерним членам в родительском классе, 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 появляется в том же месте во всех дочерних классах.


static_cast<Child*>(this)->childMember = 0; должны работать.