2009-08-09 9 views
2

Lets sagen, ich habe eine verknüpfte Liste mit einer Reihe von verschiedenen Daten drin.Gibt es eine generische Möglichkeit, über eine bestimmte Variable in einer Gruppe von Objekten zu iterieren?

class Node 
{ 
public: 
    Node* next; 
    AAA dataA; 
    BBB dataB; 
    CCC dataC; 
}; 

Gibt es eine Weise, die ich ein Iterator machen, die über was auch immer Variable iterieren würde ich angeben (und nicht für jede Variable drei separat diejenigen machen). Ich verstehe, dass der Iterator Vorlagen verwenden könnte, um die Typen AAA, BBB oder CCC zu durchlaufen, aber ich weiß nicht, wie ich angeben könnte, welche Variable zurückgegeben werden soll.

Antwort

1

Ich denke, ich habe einen Weg gefunden, ziemlich viel zu tun, was ich will, basierend auf dem Vorschlag von rstevens. Ich sah ein paar Sachen auf Klassenmitglied Zeiger nach oben und konnte die Mittelsmann Accessorklasse überspringen, indem Sie diese:

template <typename T> 
class iterator 
{ 
private: 
    Node *current; 
    T Node::*var; 

public: 
    iterator() 
     : current(NULL), var(NULL) {} 

    iterator(Node *start, T Node::*var) 
     : current(start), var(var) 
    { 
    } 

    typename T &operator *() const 
    { 
     return current->*var; 
    } 

    bool end() const 
    { 
     return (current == NULL); 
    } 

    iterator &operator++() 
    { 
     if (current) 
      current = current->next; 
     return *this; 
    } 
}; 

Und dann habe ich modifizierte Knoten Komfortfunktionen zu haben, um die Iteratoren zu machen:

class Node 
{ 
public:  
    Node* next; 
    AAA dataA; 
    BBB dataB; 
    CCC dataC; 

    typedef iterator<AAA> AIter; 
    typedef iterator<BBB> BIter; 
    typedef iterator<CCC> CIter; 

    AIter getAIter() 
    { 
     return AIter(this, &Node::dataA); 
    } 

    BIter getBIter() 
    { 
     return BIter(this, &Node::dataB); 
    } 

    CIter getCIter() 
    { 
     return CIter(this, &Node::dataC); 
    } 
}; 

So jetzt kann ich dies tun, um leicht über jedes Datenelement meiner Klasse zu iterieren:

+0

Ich mag diese Lösung nicht, weil sie für die Knoten-Klasse aufdringlich ist. – Sogartar

+0

Fühlen Sie sich frei, es nicht in Ihrem eigenen Code dann zu verwenden. :) Es löste jedes Problem, das ich vor 3 Jahren hatte, was für mich genug war. – Alex

0

Es gibt keine Möglichkeit, in C++ eine Vorlage zu erstellen, die selbst über die Elemente eines Typs iteriert. Dies erfordert einige Hilfe in der Klasse, entweder in Form von Vorlagenspezialisierungen, speziellen Methoden usw.

Die Vorlage selbst wäre ziemlich komplex und würde eine Menge Setup-Arbeit erfordern. Der Grund, warum einem bestimmten Typ für die Iteration gegeben wird, muss die Vorlage mit N anderen Typen umgehen. Namentlich der Typ der zurückgegebenen Mitglieder.

Nicht sagen, dass es nicht getan werden kann (es kann), nur dass dies komplexer als eine einfache Template-Methode ist.

0

Ich bezweifle, dass Sie Vorlagen verwenden können, um automatisch die richtige Variable auszuwählen, die zurückgegeben werden soll, außer indem Sie drei Vorlagenspezialisierungen angeben, die dem Definieren von drei Klassen entsprechen. Sie könnten jedoch eine Iterator-Klasse mit drei verschiedenen Methoden erstellen, um dataA, dataB bzw. dataC zurückzugeben (anstelle von operator *()).

+0

Könnte ich einige knifflige Arbeit Vorlagen tun und eine Getter-Methode oder Variable angeben? Wie MyIterator oder MyIterator oder etwas ähnliches? – Alex

2

Eine mögliche Lösung ist der Iterator und den Zugang in getrennte Klassen aufteilen:

Iterator Klasse, die den Zugriff auf die Daten über ein Template-Argument kapselt:

template <typename Access> 
class iterator 
{ 
private: 
    Node *current; 

public: 
    iterator(Node *start) 
    : current(start) 
    { 
    } 

    typename Access::typeof &operator *() const 
    { 
    return Access::access(*current); 
    } 

    bool end() const 
    { 
    return (current == NULL); 
    } 

    iterator &operator++() 
    { 
    if (current != NULL) 
    { 
     current = current->Next; 
    } 
    } 

    // ... other useful operators/methods 
}; 

Klassen für die verschiedenen Datenzugriff Felder. Diese können als Template-Parameter in der Iterator-Klasse verwendet werden:

class AccessDataA 
{ 
public: 
    typedef AAA typeof; 
    static AAA &access(Node &node) 
    { 
    return node.dataA; 
    } 
}; 

class AccessDataB 
{ 
public: 
    typedef BBB typeof; 
    static BBB &access(Node &node) 
    { 
    return node.dataB; 
    } 
}; 

class AccessDataC 
{ 
public: 
    typedef CCC typeof; 
    static CCC &access(Node &node) 
    { 
    return node.dataC; 
    } 
}; 

Beispiel Nutzung:

Node *start = ...; 

// Loop over B: 
for (iterator<AccessB> it(start); it++; !it.end()) 
{ 
    // ... *it ... 
} 

// Loop over C: 
for (iterator<AccessC> it(start); it++; !it.end()) 
{ 
    // ... *it ... 
} 

Eine Verbesserung wäre STL kompatibel hinzufügen semantische so Ihre Liste und Iterator kann in STL Verfahren verwendet werden, wie std :: for_each.

0

Ihre Frage ist wirklich nur eine Teilfrage.

Sie könnten einen Iterator-Adapter erstellen, der so funktioniert, als ob er über eine Sammlung von AAA iteriert hätte, aber er hat tatsächlich über eine Sammlung von Node s iteriert. Dies ist jedoch möglicherweise nicht die beste Lösung für Ihr zugrunde liegendes Problem.

Ich vermute, dass Sie eine Art von Aktion haben, die Sie auf jedem Mitglied durchführen möchten. Angenommen, das war ein solcher Funktor.

struct DoAAAAction 
{ 
    void operator()(AAA& a); 
}; 

Es ist wahrscheinlich einfacher, die Aktion zur Anpassung auf einem Node zu handeln.

template<class Action> 
class DataA_ActionAdapter 
{ 
public: 
    DataA_ActionAdapter(Action aa) : a(aa) {} 
    void operator()(Node& n) { a(n.dataAAA); } 
private: 
    Action a; 
}; 

Auf diese Weise können Sie Standard-Algorithmen auf Node Iteratoren verwenden.

template<class NodeIterator, class AAAAction> 
void TestAAA(NodeIterator first, NodeIterator last, AAAAction aaaa) 
{ 
    std::for_each(first, last, DataA_ActionAdapter<AAAAction>(aaaa)); 
} 
0

Wenn ich Sie richtig verstehe, sind Sie auf iterieren dataA, DataB und DataC - so bedeutet dies, dass AAA, BBB und CCC alle den gleichen Basistyp (oder zumindest ähnliche Merkmale aufweisen). Warum nicht einfach diese in einem std :: vector oder std :: set speichern?

Hinweis: AAA, BBB und CCC sind alle aus NodeType

abgeleitet
class Node 
{ 
public: 
    Node() 
    { 
     dataNodes.push_back(AAA()); 
     dataNodes.push_back(BBB()); 
     dataNodes.push_back(CCC()); 
    } 

    // AAA dataA; 
    // BBB dataB; 
    // CCC dataC; 

    std::vector <NodeType> dataNodes; 

    std::vector <NodeType>::iterator begin() 
    { 
     return dataNodes.begin(); 
    } 

    std::vector <NodeType>::iterator end() 
    { 
     return dataNodes.end(); 
    } 
}; 
3

Der beste Weg, die ich gefunden habe, dies zu tun ist mit boost bind und boost transform_iterator

Zuerst Sie brauchen Sammlung von Node-Objekten und einem Iterator, der die Sammlung durchquert. In meinem Beispiel verwende ich zur Abkürzung eine std :: list.

Verwandte Themen