Доступ к дочерним элементам в родительском классе, C ++ - PullRequest
7 голосов
/ 25 августа 2011

Я сталкиваюсь с ситуацией, когда мне нужно получить доступ к дочерним переменным-членам внутри родительского класса. Я знаю, что это противоречит принципам ОО, но мне приходится иметь дело со сценарием, в котором сотни классов наследуются от одного, и половина из них перестала использовать одну из родительских переменных, а также объявила и использовала свои собственные (нужно было переключиться от int до int [] и, очевидно, человек, который сделал это, не принял во внимание применение этих изменений в родительском классе).

Один из вариантов - иметь виртуальную функцию для работы с ней, но это означает, что мне нужно изменить код в сотнях файлов / объектов и протестировать каждый из них. Поэтому я подумал, что если можно использовать магию указателей С старой школы для получения доступа к этим переменным внутри родительского метода, это избавит от необходимости сотен виртуальных функций.

В основном это то, чего я хочу достичь:

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

class Child1 : Parent
{
    int childMember;
}

class Child2 : Parent
{
    int childMember;
}

Пожалуйста, дайте мне знать, если это вообще возможно. Если да, то как мне этого добиться.
Другие предложения приветствуются, но имейте в виду, что я хотел бы вносить изменения только в родительский класс.
ТИА.

Ответы [ 9 ]

7 голосов
/ 25 августа 2011

Единственный чистый способ - использовать подход виртуальной функции.

Если у класса 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 не будет работать. Хотя любой класс, предназначенный для наследования, должен иметь хотя бы одну виртуальную функцию, даже если это просто деструктор.)

4 голосов
/ 25 августа 2011

Вероятно, здесь поможет 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();
    }
}

Я изменил код, чтобы он был компилируемым - возможно, не тем, что вам нужно, - но затем предоставил лучший (= компилируемый) пример кода.

2 голосов
/ 17 мая 2016

Если вам разрешено изменять исходный код ваших дочерних классов, вы можете сделать что-то вроде этого:

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 () в сочетании с typeid C ++ 11, потому что он примерно в три раза быстрее, чем 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;
    }
};
1 голос
/ 06 апреля 2013

Я не получаю отрицательных голосов за статический актерский состав. Следующие работы:

#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, если вы нашли правильного потомка. Я нахожу этот подход также полезным, потому что класс Child (B) почти не изменяется.

0 голосов
/ 25 августа 2011

Будет ли вариант создания промежуточного абстрактного класса, который содержит 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() { }
}
0 голосов
/ 25 августа 2011

Вы можете использовать любопытно повторяющийся шаблон для достижения этой цели.

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>;
};
0 голосов
/ 25 августа 2011

Очевидно, у вас есть некоторое подмножество производных классов, которые используют 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
}
0 голосов
/ 25 августа 2011

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

0 голосов
/ 25 августа 2011

Не существует безопасного и надежного способа сделать это с помощью указателей. Вы можете взломать с умными смещениями указателя, но это будет зависеть от childMember, появляющегося в том же месте во всех дочерних классах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...