2010-08-17 2 views
1

Итак, пусть у меня есть класс дерева, как это в C++C++ обработки производного класса, который сам себе ссылку

class Node{ 
    void addChild(Node*); 
    /*obvious stuff*/ 
    protected: 
     Node* parent; 
     vector<Node*> children 
} 

class specialNode : public Node{ 
    void addChild(specialNode*); 
    /*obvious stuff*/ 
    /*special stuff*/ 
} 

Теперь, когда я получить доступ к детям в specialTree, я, очевидно, получить Node *, не specialNode *.

Но этот specialNode * имеет переменные-члены и функции, которые Node не имеет.

Я могу заставить specialNode принимать специальные значения только как дети и в противном случае ломать время компиляции, , но я все равно получаю Node * при доступе к дочерним/родительским элементам, и я должен использовать его, когда я хочу использовать специальные функции, даже в специальных функциях.

Есть ли какой-нибудь умный или просто лучший способ сделать это? За исключением буквально литья каждый раз?

ответ

4

Если вам нужно только SpecialNode объекты в дереве (и просто хотите, чтобы инкапсулировать все общие функции дерева в Node) вы можете сделать Node так называемый «микс в» класса как

template <class N> 
class Node : public N { 
public: 
    void addChild(Node<N>*); 
protected: 
    Node<N>* parent; 
    vector<Node<N>*> children; 
}; 

class SpecialNodeBase { 
    // Here comes all "special" data/methods for your "special" tree 
}; 

typedef Node<SpecialNodeBase> SpecialNode; 

После этого вы можно построить дерево SpecialNode объектов и использовать все методы из SpecialNodeBase, а также дополнительных древовидных управления функциями от Node

+0

Ничего себе ... это очень мило. Именно то, что я искал. Спасибо. – jaehoony

+0

Я мог бы отметить, что, сделав это, вы эффективно победили цель наследования - 'SpecialNode' больше не выводится из' Node', вместо этого вы создаете независимые классы с тем же набором переменных-членов. – casablanca

+0

Я тоже собирался указать на это «обращение к наследству» :). Фактически, микширование может использоваться для моделирования ортогональных (сквозных) функций, для которых наследование будет субоптимальным: предположим, что вы хотите организовать автомобили и людей в дереве. Ни автомобили, ни люди не являются «Узлом» (это наследование означает). Использование 'Node ' resp. 'Node ' понятие может быть прочитано как «Узел автомобиля» и «Узел человека», который более подходит для этой ситуации. – MartinStettner

0

Поскольку функция AddChild в классе ребенка не полиморфизм, сделать его виртуальным, но перегружать функции через базовые/дочерние элементы не допускаются, так что мы должны изменить параметр AddChild в дочернем классе:

class Node{ 
    virtual void addChild(Node*); 
    ... 
} 

class specialNode : public Node{ 
    virtual void addChild(Node*); 
    ... 
} 

Теперь он должен работать.


Если вы хотите получить доступ к переменной childeren из дочернего класса (specialNode класса), вы должны бросить его. Например:

specialNode* var = static_cast<specialNode*>(children[i]); 

Поскольку мы объявили AddChild как виртуальную функцию, то мы должны использовать dynamic_cast вместо static_cast, если мы не уверены в том, что children[i] всегда является экземпляром класса specialNode, и, таким образом, лучше использовать dynamic_cast:

specialNode* var = dynamic_cast<specialNode*>(children[i]); 
if(var != NULL) 
{ 
    //... 
} 
+0

Спасибо за ваш ответ .. но не совсем о чем вопрос. Кроме того, я уверен, что addChild (specialNode *) будет просто охватывать область добавления addChild (node ​​*) – jaehoony

-1

вы обязательно должны забрасывать Node * к specialNode * в какой-то момент, но вы можете сделать это чистый и простой в управлении, делая это в одном месте. Можно добавить функцию-член, скажем getParent и переопределить его в specialNode, как это:

class Node { 
    ... 
    virtual Node *getParent() { 
    return parent; 
    } 
}; 

class specialNode : public Node { 
    ... 
    specialNode *getParent() { 
    return dynamic_cast<specialNode *>(parent); 
    } 
}; 

Конечно, это предполагает, что specialNode s всегда есть другие specialNode S в качестве родителей/детей. Если вы смешаете Node s и specialNode s, это явно не сработает.

+0

Вы столкнетесь с проблемами, если вы определите две иначе идентичные функции с разными типами возврата (есть два метода getParent которые различаются только по типу их возвращаемого значения) – MartinStettner

+1

@MartinStettner: взгляните на [Covariant Return Types в C++] (http://www.lwithers.me.uk/articles/covariant.html) - это разрешено в случай производных классов. – casablanca

+0

Вы правы, я ошибся, извините. – MartinStettner

0

Если я правильно понимаю, класс решение «Mix-в» не позволит вам позвонить addChild из функций, реализованных SpecialNodeBaseClass.

Вы действительно можете сделать следующее:

template <class recursiveT> 
class Base { 
public: 

    Base(dataType data) { populate children with data; } 

    void addChild() { something base class appropriate; } 

protected: 
    std::vector<recursiveT> children; 
}; 




class Derived: public Base<Derived> { 
public: 
    /* note: the constructor here will actually call the 
     constuctor of the base class */ 
    Derived(dataType data) : Base<Derived>(data) {} 
    /* other special functions go here. */ 
}; 

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

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