2013-07-15 2 views
2

у меня есть следующие иерархии классов для графов:вопрос дизайна иерархии наследования классов

typedef vector<int> ArrayI; 
typedef vector<Array<long>> Mat2DB; 
typedef vector<ArrayI> adjList; 

class baseGraph { 
    int nodes; 
    ArrayI degree; 
    //some member functions. 
} 

class matGraph: public baseGraph { 
    Mat2DB matrix; 
    //member functions. 
} 

class lMatGraph: public matGraph { 
    ArrayI labels; 
    //member functions. 
} 

class listGraph: public baseGraph { 
    adjList list; 
    //member functions. 
} 

class lListGraph: public listGraph { 
    ArrayI labels; 
    //member functions. 
} 

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

Например, у меня есть функция sssp(int node), которая реализует кратчайший путь с одним источником. Реализация различна для class matGraph и class listGraph, которые представляют собой представление матрицы смежности и представление списка смежности соответственно графиков. Теперь не нужно изменить определение для меченого версии этих графиков, так что я не определяю эти функции снова в lListGraph и lMatGraph

Теперь единственная проблема, я Хавин это с setLabel(const ArratI &) в lListGraph и lMatGraph классов. Мне нужно, чтобы эта функция была виртуальной, чтобы она вызывалась через указатель базового класса, но в то же время у меня нет ничего такого, как метки для классов matGraph и listGraph.

Я не знаю, правильна ли моя иерархия дизайна или нет, но она показалась мне интуитивной. Поэтому любые комментарии по этому вопросу были бы хорошими. Что я могу сделать с помощью функции setLabel. Хорошо ли иметь такую ​​функцию (для меня это выглядит как обходной путь, поэтому этот вопрос), или мне нужно пересмотреть мою иерархию классов.

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

EDIT:

Использование класса графа используется в другом классе clustering, где я есть член baseGraph *graph т.е.

class clustering { 
    baseGraph *graph; 
} 

Я хранить указатель на базовый класс здесь, так что я могу использовать различные алгоритмы (реализованы как функции) от class graph. Для класса кластеризации он снова зависит от того, какой тип графика я хочу использовать.

+0

Помните, что 'vector ' специализируется на отличном от всех других стандартных контейнерах (возможно, вы это предполагали). –

+0

@MarkB В моей оригинальной реализации я использую 'vector ' для моделирования взвешенных графиков, так что это нормально, но рад узнать разницу. Набрав вопрос, я решил сделать иерархию простой. Я отредактирую вопрос. –

+0

Должен ли ярлык меняться несколько раз во время выполнения? Или он останется постоянным для жизни объекта? –

ответ

1

Возможно это?

typedef vector<int> ArrayI; 
typedef vector<Array<long>> Mat2DB; 
typedef vector<ArrayI> adjList; 

class baseGraph { 
    int nodes; 
    ArrayI degree; 
    virtual void sssp(int node); 
    //some member functions. 
} 

class labeledGraph: public virtual baseGraph { 
    ArrayI labels; 
    virtual void setLabel(const ArratI &); 
    //member functions. 
} 

class matGraph: public virtual baseGraph { 
    Mat2DB matrix; 
    //member functions. 
} 

class lMatGraph: public virtual matGraph, public virtual labeledGraph { 
    //member functions. 
} 

class listGraph: public virtual baseGraph { 
    adjList list; 
    //member functions. 
} 

class lListGraph: public virtual listGraph, public virtual labeledGraph { 
    //member functions. 
} 

Я предполагаю, что здесь, что вы неправильно унаследовали от графика, когда вы должны были унаследовать от baseGraph (typeo) - хотя даже если не сводится к одной точке.

Также грубое кодирование, если у вас есть вопросы или если есть ошибки, не стесняйтесь спрашивать.

+0

Извините, если это наивно, но я никогда раньше не использовал виртуальное и множественное наследование. Не могли бы вы объяснить, как я могу вызвать 'setLabels' с указателем' baseGraph'. –

+0

Если вы написали _have_ через 'baseGraph *', тогда нет опции, но иметь его на 'class baseGraph', и в этом случае применяются другие ответы. По моему предложению вы назовете его через «labeledGraph *», поскольку имеет смысл работать с 'labelGraph *', когда есть возможность установки меток. Не могли бы вы сделать кластеризацию шаблона? 'template кластеризация {/*...*/ std :: unique_ptr < GraphType > * graph; } '? в этом случае вы можете посмотреть некоторые параметры проверки существования setLabel на тип шаблона, прежде чем приступать к его использованию. –

+0

Раньше я не думал о кластеризации класса шаблонов, но теперь я вроде как начинаю любить эту идею. Единственная проблема, о которой я думаю, будет заключаться в том, что если мне нужно было изменить свое представление графика (это возможный случай) в ходе моей программы, то я не смогу это сделать. –

0

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

  • Ничего не делать - просто игнорировать запрос для установки метки
  • сгенерирует исключение (например, abort) - что-то, вероятно, неправильно, так пользователь должен это знать!

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

Обратите внимание, что если вы продолжаете добавлять материал к базовому классу, который соответствует каждому производному классу, вы получите в конечном итоге a lot of mess в базовом классе - ничего хорошего!


Кроме того, нижеследующее может решить вашу проблему с setLabel и сделать вашу иерархию классов «здоровой».

Рассмотрите возможность перемещения основных алгоритмов, таких как sssp, в сторону от объявлений классов - сделать их перегруженными автономными функциями вместо функций-членов. Таким образом, вам не нужно объявлять sssp в базовом классе. Если вы примете это руководство, когда вы реализуете новый алгоритм, компилятор будет проверять все вызовы функций и выдавать ошибку, если она отсутствует (это лучше, чем сбой или получение неверных результатов).

class baseGraph { 
    int nodes; 
    ArrayI degree; 
    // a minimum number of member functions (e.g. getNode; getEdges) 
} 

class matGraph: public graph { 
    Mat2DB matrix; 
} 

class lMatGraph: public matGraph { 
    ArrayI labels; 
    void setLabel(const ArrayI &); 
} 

int sssp(const matGraph& graph, int node) 
{ 
    // Some code 
} 

int sssp(const lMatGraph& graph, int node) 
{ 
    // Some code; here you can use labels 
} 

Это обсуждается в книге Эффективное использование C++ (Effective C++ Item 23 Prefer non-member non-friend functions to member functions)

+0

Я отредактировал вопрос, чтобы добавить более подробную информацию. Дело в том, что если у меня есть функция non member, тогда класс кластеризации будет полным беспорядком, потому что я должен сам позаботиться о каждом типе графа. –

0

Все сводится к простому выбору. Что произойдет, если я попытаюсь установить метку в графе, который на самом деле не поддерживает метки?

  1. Ничто (попытка может быть зарегистрирован, но в противном случае игнорируется)
  2. Катастрофический провал
  3. я не должен быть в состоянии даже попробовать (компилятор не должен позволить мне)

Это Это. Это все ваши варианты.

Первые два варианта просты, вы просто пишете виртуальную функцию, сообщающую об ошибке (регистрирует ее или генерирует исключение).

Третий интересен. Это означает, что никакой виртуальной функции вообще нет. Не в вашем классе, а не в каком-либо базовом классе. Это противоречит вашему дизайну, но ваш дизайн не обязательно идеален.

Итак, как вы устанавливаете этикетку? Через то, что не является указателем на ваш базовый класс :) Он может быть указателем на другой базовый класс (mixin - вы используете множественное наследование для добавления функциональности маркировки к графикам). Или вы можете templatize ваш дизайн, чтобы иерархия не имеет большого значения, и вы всегда статически узнаете наиболее производный тип ваших объектов.

Смежные вопросы