Как правильно спроектировать иерархию классов, используя указатели в C ++ - PullRequest
0 голосов
/ 26 мая 2010

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

Дело в том, что в простой иерархии, в которой у вас есть класс A с указателями на класс B и последний на класс C. Не путайте свойство наследования в объектно-ориентированном программировании, но в основном я говорю что класс C является потомком класса B, а класс B является потомком класса A.

Дело в том, что я хочу иметь доступ напрямую из класса А к классу С (внук по аналогии) с указателями. Некоторые другие члены указали, что это плохой дизайн, в основном потому, что если удалить экземпляр класса C из коллекции класса B, то в коллекции класса A останется указатель на «ничто». Тогда как это смоделировано правильно?

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

Julen.

Ответы [ 3 ]

5 голосов
/ 26 мая 2010

К сожалению, вы просто изобретаете велосипед. Измените свой подход, все, что pointer-stuff может фактически управляться автоматически.

Если возможно, используйте состав . Пусть объект class A содержит объект class B, который будет содержать объект class C. Этот очевидный и простой подход может сэкономить вам огромное количество времени и сделать структуру вашего приложения лучше и «чище».

Если к вашим классам можно обращаться с объектами одного типа (например, если они совместно используют один и тот же интерфейс), то выведите ваш C из B и получите B из A. Сразу после этого вам придется подумать об их общем интерфейсе - с точки зрения virtual functions в вашем class A, который действует как базовый класс. Это действительно сложный момент, но как только вы привыкнете планировать свои интерфейсы таким образом, вы просто не сможете думать о чем-то другом.

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

2 голосов
/ 26 мая 2010

Вместо того чтобы информировать класс A о каждом классе C, рассмотрите возможность использования Composite Pattern :

#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <stdexcept>

//------------------------------------------------------------------------------
class Component
{
public:
    typedef boost::ptr_vector<Component> Children;

    virtual void print() = 0;
    // Other operations

    virtual bool isLeaf() {return true;}

    virtual Children& children()
        {throw std::logic_error("Leaves can't have children");}
};

//------------------------------------------------------------------------------
class Composite : public Component
{
public:
    void print()
    {
        BOOST_FOREACH(Component& child, children_)
        {
            child.print();
        }
    }

    bool isLeaf() {return false;}

    Children& children() {return children_;}

private:
    Children children_;
};

//------------------------------------------------------------------------------
class Nut : public Component
{
public:
    Nut(std::string info) : info_(info) {}
    void print() {std::cout << info_ << std::endl;}

private:
    std::string info_;
};

//------------------------------------------------------------------------------
class Bolt : public Component
{
public:
    Bolt(std::string info) : info_(info) {}
    void print() {std::cout << info_ << std::endl;}

private:
    std::string info_;
};

//------------------------------------------------------------------------------
class Wheel : public Composite
{
public:
    Wheel(std::string info) : info_(info) {}
    void print()
    {
        std::cout << info_ << std::endl;
        Composite::print();
    }

private:
    std::string info_;
};

//------------------------------------------------------------------------------
class Vehicle : public Composite
{
public:
    Vehicle(std::string info) : info_(info) {}
    void print()
    {
        std::cout << info_ << std::endl;
        Composite::print();
        std::cout << "\n\n";
    }

private:
    std::string info_;
};

//------------------------------------------------------------------------------
int main()
{
    Wheel* wheel1 = new Wheel("Wheel1");
    wheel1->children().push_back(new Nut("Nut11"));
    wheel1->children().push_back(new Nut("Nut12"));
    wheel1->children().push_back(new Bolt("Bolt11"));
    wheel1->children().push_back(new Bolt("Bolt12"));

    Wheel* wheel2 = new Wheel("Wheel2");
    wheel2->children().push_back(new Nut("Nut21"));
    wheel2->children().push_back(new Nut("Nut22"));
    wheel2->children().push_back(new Bolt("Bolt21"));
    wheel2->children().push_back(new Bolt("Bolt22"));

    Vehicle bike("Bike");
    bike.children().push_back(wheel1);
    bike.children().push_back(wheel2);

    bike.print();
}

Эта программа выводит:

Bike
Wheel1
Nut11
Nut12
Bolt11
Bolt12
Wheel2
Nut21
Nut22
Bolt21
Bolt22

Обратите внимание, что когда вызывается bike.print(), print вызывается рекурсивно для всех детей. Вот как вы можете выполнять операции на всех дочерних элементах, когда дед не знает обо всех дочерних элементах.

Шаблон Visitor очень хорошо работает с шаблоном Composite, поэтому я советую вам также ознакомиться с ним. Особенно, если у вас много операций, которые могут быть реализованы в терминах более простых.

1 голос
/ 26 мая 2010

Я думаю, что вы говорите, что класс C происходит от класса B, который происходит от класса A. Не путайте иерархию классов с указателями на объекты класса. Вы можете делать то, о чем говорите, с помощью одного объекта (и одного указателя на объект). Если вы создаете указатель типа C * на объект типа C, вы можете получить доступ ко всем открытым и защищенным методам, определенным в классах B и A. Альтернативно, если вы реализуете виртуальные методы, вы можете создать указатель типа A * на объект тип C. Во время выполнения виртуальная таблица будет использоваться для автоматического поиска реализации этого виртуального метода самого низкого уровня (т. е. из класса C, если он определен там, или из класса B, если он определен там, или из класса A). Существует множество хороших ресурсов для изучения основ C ++. Я уверен, что есть темы на SO с таким списком, если вы ищете.

EDIT

Вот одна из таких ссылок: https://stackoverflow.com/questions/70159/what-is-the-best-source-to-learn-c

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