|
|
|
|
Programmieren mit C++Borland C++32-Bit-ProgrammierungIterator durch List-Container
|
Wie realisiert man einen Iterator durch Container-Klassen?
| Frage | |
In einer Anwendung, die Container verwendet, sollte man zunächst eine Klasse deklarieren, die einen Item in einem Container repräsentiert.
class CMyClass : public CObject {
// programmspezifische Member
...
};
typedef CTypedPtrList
Eine separate Klasse enthält das tatsächliche Container-Objekt.
class CMyClassList : public CObject {
private:
TMyList List;
// Ggfs. andere Datenmember
public:
// übliche Methoden (Konstruktor, Destruktor etc.
void Flush(void);
BOOL Add(CMyClass *ptr);
CMyClass *Find(...);
BOOL Del(...);
};
Ist der Container mit einem Interface-Objekt wie Tree-Control, List-Control oder anderen verbunden, muß eine Iteration durch den Container möglich sein, damit Aktionen für jedes Element des Containers, abhängig von einer Bedingung, erfolgen können. Hierzu sind verschiedene Ansätze denkbar.
| Lösung | |
Die einfachste Lösung wäre, eine Listen-Variable im öffentlichen Teil der Deklaration zu plazieren, so daß anschließend über dieses Member ein direkter Zugriff auf den Container möglich wäre. Allerdings verstößt dieser Ansatz gegen das fundamentale OOP-Gebot der Kapselung.
| Öffentliche List-Variable | |
Eine andere Vorgehensweise besteht darin, eine öffentlich deklarierte Member-Funktion zu verwenden, die über eine ebenfalls bereitgestellte Callback-Funktion die einzelnen Elemente bearbeitet. Dies ist jedoch etwas verwirrend, da die bereitgestellte Callback-Funktion üblicherweise eine Member-Funktion einer anderen Klasse ist, so daß für jede Callback-Funktion in der Klasse CMyClassList eine überladene Member-Funktion vorhanden sein muß. Ist die Callback-Funktion hingegen eine statische Non-Member-Funktion, so muß ein DWORD-Argument zur Übergabe des „this“-Zeigers mitgeführt werden, der innerhalb der Klasse auf einen Zeiger für das entsprechende Objekt gecastet werden muß, bevor man eine Methode aufrufen kann, die dann tatsächlich die Aktion auf das Container-Element ausführt.
| Callback-Funktion | |
Wesentlich einfacher und eleganter ist hingegen der Einsatz einer speziellen Iterator-Klasse, wodurch sich die Klassen-Deklaration etwas verändert.
class CMyClassListIterator;
class CMyClassList : public CObject {
private:
TMyClassList List;
// Ggfs. weitere Member und Daten
public:
// übliche Methoden (Konstruktor, Destruktor etc.
void Flush(void);
BOOL Add(CMyClass *ptr);
CMyClass *Find(...);
BOOL Del(...);
// Neu: Iterator-Member
friend CMyClassListIterator;
};
Die neue Iterator-Klasse ist wie folgt definiert:
class CMyClassListIterator : public CObject {
private:
CMyClassList& Owner;
POSITION Pos;
public:
CMyClassListIterator(TProcess& obj) : CObject(),Owner(obj)
{ Reset(); }
void Reset(void)
{ Pos = Owner.List.GetHeadPosition(); }
void Next(void)
{ Owner.List.GetNext(Pos); }
CMyClass *Current(void)
{ return (CMyClass*)Owner.List.GetAt(Pos); }
BOOL IsDone(void)
{ return (Pos == NULL) ? TRUE : FALSE; }
};
Alles, was jetzt noch notwendig ist, um die Liste zu durchlaufen, ist der nachfolgende Codeblock:
| Iterator-Klasse |
CMyClassList MyList;
........
CMyClassListIterator iterator(MyList);
while (!iterator.IsDone()) {
CMyClass *ptr = iterator.Current();
iterator.Next();
};
Auf ptr kann beliebig zugegriffen werden, jedoch darf ptr nicht gelöscht werden. Der Inhalt, auf den der Zeiger verweist, kann beliebig manipuliert werden. Es ist ebenso möglich, geschachtelte Iteratoren zu implementieren. Ein positiver Seiteneffekt dieser Implementation ist, daß der komplette Code für das Durchlaufen und Bearbeiten der Containerelemente in einer Funktion zusammengefaßt ist und insbesondere keine Callback-Funktionen anfallen. Des weiteren leistet diese Vorgehensweise auch eine Datenkapselung, da das Listenobjekt nicht direkt sichtbar ist.
| Code zum Durchlaufen der Liste
|
|
|