Вызов переопределенных методов класса как цепочки в C ++ - PullRequest
0 голосов
/ 19 января 2012

более чем в одном случае я чувствовал необходимость определить методы класса, которые вызываются способом, аналогичным конструкторам или деструкторам.

Конкретный пример будет; в программе у меня была очень сложная сеть узлов разных типов, которые взаимно зависели друг от друга очень нерегулярно (сеть вообще не напоминала дерево). Когда узел должен был быть уничтожен, он запускал сложную цепочку разрушений в сети. Очень похоже на паутину, которая разрывается на части, но сложнее.

Во время выполнения этой цепочки управление возвращалось к методам инициатора (или одного из промежуточных элементов в цепочке), так что фактическое уничтожение должно было произойти, когда цепочка установилась, и поэтому Я не мог использовать деструкторы для этой цели. Однако в иерархии классов моих узлов мне был нужен «подобный деструктору», то есть лестничный способ вызова моей неразрушающей функции перед уничтожением (по тем же причинам, почему фактический деструктор также называется таким образом, а именно: каждый шаг в иерархии классов должен был вносить свой вклад в цепочку).

Я закончил кодировать лестницу вручную. А именно, класс nodeBase имеет метод preDestroyNodeBase, который выполняет свою работу и вызывает виртуальный метод preDestroyNode и т. Д. До конца (я знаю, так он выглядит как конструктор, но он был сравнительно более элегантным). таким образом, поскольку вы можете просто вызвать «preDestroy» самого базового класса).

Вы можете себе представить, насколько подвержен ошибкам этот подход, не говоря уже о безобразии. Есть ли более чистый способ эмуляции конструктора или деструктора для вызова методов? Какая-то магия шаблонов или даже макрокоманда! Поскольку ручное кодирование слишком чревато ошибками даже для одного программиста, поэтому я не могу представить, что такое поведение будет передано клиентам библиотеки.

Может быть, я упускаю фундаментальную концепцию программирования, которая устраняет необходимость в таких функциях. Если это так, я был бы рад, если бы вы указали, как еще можно обработать этот пример сети узлов.

Большое спасибо!

Ответы [ 3 ]

2 голосов
/ 19 января 2012

Когда вы объявляете виртуальную функцию, она будет вызывать наиболее производный обработчик функции, а затем оттуда у вас будет каждый вызов обработчика NextMostDerivedClass::preDestroy, и вызовы перейдут к следующему наиболее производному обработчику функции и вы можете снова позвонить NextMostDerivedClass::preDestroy и так далее. Это тот же путь, по которому идет виртуальный деструктор, за исключением того, что вам не нужно ничего вызывать вручную с деструкторами, он автоматизирован. Если бы вы поместили операторы cout из приведенного ниже примера кода в ваши деструкторы, вы могли бы увидеть тот же вывод, что и в приведенном ниже примере.

#include <iostream.h>

class Foo
{
    public:
    virtual void PreDestroy()
    {
        cout << "Foo preDestroy";
    }
}

class Bar : public Foo
{
    public:
    void PreDestroy()
    {
        cout << "Bar preDestroy\n\n";

        Foo::PreDestroy();
    }
}

class MostDerived : public Bar
{
    public:
    void PreDestroy()
    {
        cout << "MostDerived preDestroy\n\n";

        Bar::PreDestroy();
    }
}

int main() 
{
    MostDerived testObj;

    testObj.PreDestroy();
}

Вывод должен быть:

MostDerived preDestroy

Бар преДестрой

Foo preDestroy

0 голосов
/ 19 января 2012

Вы можете использовать шаблон посетителя для просмотра графа узлов (я полагаю, вы говорите о графике) и позволить узлам поместить себя в своего рода «подлежащий удалению» список (при необходимости), который удерживается объектом посетителя. Во второй итерации вы можете просмотреть элементы списка и вызвать для них некий метод «уничтожить», который, в свою очередь, выполнит окончательную очистку. При таком подходе все узлы должны поддерживать шаблон посетителя и содержать этот метод "destroy".

0 голосов
/ 19 января 2012

Я бы использовал механизм подписки, где, если вы хотите, чтобы a содержал указатель на другой объект, вы также должны подписаться на событие их уничтожения; при получении такого события отмените его подписку на собственное событие и очистите свои собственные структуры (что может затем привести к дальнейшему разрушению).

т.е. (C ++ 1x синтаксис для краткости)

struct node {
    ~node() { for(auto i : subscribers) i->notify(this); }
    void subscribe(node *n) { subscribers.push_back(n); }
    void notify(node *n) { subscribers.remove(n); /* Handle other node being removed */ }

private:
    std::list<node *> subscribers;
}

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

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