Forum: PC-Programmierung Ein Objekt in 2 voneinander unabhängigen verketteten Listen?


von Alexander I. (daedalus)


Lesenswert?

Hallo,

folgende C++ Problematik:

Es existiert eine Template-Klasse List für verkettete Listen
1
template<class T>
2
class List
3
{
4
  static T* First;
5
  static T* Last;
6
  T* Next;
7
  // ...
8
}

Nun werden andere Objekte davon abgeleitet und bei deren Konstruktion 
automatisch in der Liste verkettet. In diesem Fall sind es verschiedene 
Treiberobjekte, die von List abgeleitet werden:
1
class GenericDriver : public List<GenericDriver>
2
{
3
 static void InitAll(void);
4
 static void UpdateAll(void);
5
 virtual void Init(void) = 0;
6
 virtual void Update(void) = 0;
7
 // ...
8
}

Das hat jetzt den Charme, dass man z.B. alle Treiber an einer Kette 
durchlaufen kann, beispielsweise GenericDriver::InitAll(); aufruft und 
alle Treiber initialisieren sich mit ihrer eigenen Init()-Funktion. Ein 
weiteres Beispiel wäre der millisekündliche Aufruf von Update() über 
UpdateAll();

Das Konzept ist soweit gut, funktioniert seit einigen Jahren prima. 
Jetzt ergibt sich aber ein Problem:

Eine von GenericDriver abgeleitete Klasse, nennen wir sie 
MySpecialDriver benötigt eine eigene Liste, die alle Instanzen von 
MySpecialDriver verkettet aber die Basisklasse GenericDriver davon 
unabhängig ja immer noch verkettet bleiben soll. Da die Basisklasse 
GenericDriver ja bereits von List abgeleitet ist, kann ich die 
MySpecialDriver nicht nochmal von List ableiten, sonst werden die 
List-Anteile (Next-Pointer) von Basisklasse und abgeleiteter Klasse ja 
"ambitious".

Wie könnte ich das elegant machen, dass ich das gleiche List-Template 
verwende und dasselbe Treiberobjekt damit 2 unterschiedlichen und 
voneinander unabhängigen verketteten Listen angehört?

: Verschoben durch User
von Uli T. (avaron)


Lesenswert?

Verwende die Liste nicht als Basisklasse, sondern nutze sie als 
Member-Variable.
Ansonsten sieh Dir mal das Design-Pattern "Interfaces" an, damit kann 
man solch eine alles-abarbeiten Logik auch wunderschön implementieren.

Allerdings eher generell: Du schreibst diesen Code für kleine MCUs? 
Dabei finde ich Templates etc. eigentlich ganz schon oversized. Hast Du 
Dir mal die entstehenden Code-Größen angesehen?
Ich finde C++ ist eine wundervolle, mächtige Sprache für die 
Applikationsentwicklung auf Desktop-Rechnern, hat aber meiner Ansicht 
nach in der embedded Entwicklung nur was zu suchen wenn man
a) genau weiß was man macht und welche Auswirkungen das hat und
b) eine MCU mit halbwegs Leistung zur verfügung har.

Hier auf Arbeit haben wir recht häufig Performanceprobleme, da 
Firmwarekomponenten in (schlechtem) C++ geschrieben sind. Und hier 
werkelt immerhin ein ARM9-Core @100Mhz...

von Alexander I. (daedalus)


Lesenswert?

Ja, dass C++ uU mehr Code produziert als C und man sich von der 
PC-Progammierweise verabschieden muss ist mir klar. In diesem Fall macht 
es aber durchaus Sinn und ist mehr die Ausnahme als die Regel. Der 
Prozessor ist dick genug, auch Codegrößen und Speicherverbrauch stellen 
kein Problem dar.

Danke für den Tipp mit den Interfaces, ich lese mich mal ein.

von Udo S. (urschmitt)


Lesenswert?

Alexander I. schrieb:
> Es existiert eine Template-Klasse List für verkettete Listen [Beispiel]
> Nun werden andere Objekte davon abgeleitet und bei deren Konstruktion
> automatisch in der Liste verkettet.

Wenn du Objekte von einer Liste ableitest dann sind die abgeleiteten 
Objekte auch Listen und werden nicht in irgendeine Liste eingehängt.
Irgendwie bist du auf dem falschen Dampfer.

Du hast 2 unabhängige Listen. Dann instanziiere die und hänge deine 
Objekte einfach in die Liste(n) ein in die sie sollen.

von Karl H. (kbuchegg)


Lesenswert?

Da stimmt was nicht.
1
template<class T>
2
class List
3
{
4
  static T* First;
5
  static T* Last;
6
  T* Next;
7
  // ...
8
}

für dich ist eine Liste und ein Element in der Liste dasselbe. Und genau 
das ist dann der Punkt an dem dann meistens die Probleme anfangen. 
Trenne die Dinge. Auf der einen Seite gibt es Listen und auf der anderen 
Seite gibt es Knoten in der Liste. Das sind 2 verschiedene Dinge.

von Alexander I. (daedalus)


Lesenswert?

KHB:
Genau so ist es. Diese Listen-Implementierung ist recht speziell und 
stammt nicht aus meiner Feder. Da die hier im Unternehmen aber zigfach 
verwendet wird, versuche ich damit klarzukommen :-/

Udo:
Doch sie werden Listenelemente, weil im Konstruktor von List das neue 
"List" automatisch verkettet wird. Die Liste an sich bleibt nur eine 
Liste, da First und Last static sind. Ist eine sehr ungewöhnliche 
Implementierung, ich weiß...

von Karl H. (kbuchegg)


Lesenswert?

Alexander I. schrieb:
> KHB:
> Genau so ist es. Diese Listen-Implementierung ist recht speziell und
> stammt nicht aus meiner Feder. Da die hier im Unternehmen aber zigfach
> verwendet wird, versuche ich damit klarzukommen :-/

Dann solltest du das anleiern, dass das geändert wird.
Ist genauso wie die Unsitte, den Listeniterator in Form eines 'current' 
Elements in die Liste selbst mit aufzunehmen. Das sieht auch auf den 
ersten Blick ganz toll aus. Die Probleme kommen aber im Laufe der Zeit.

von Udo S. (urschmitt)


Lesenswert?

Hallo,
ich habe mir das jetzt genauer angeschaut, mich hatte der Name List auf 
die falsche Fährte gebracht, weil ich hauptsächlich Jave programmiere 
und da solche generischen Container per se zur Verfügung stehen.

Ich kann Karl Heinz nur zustimmern. Macht ein sauberes Design.
Eine Element ist ein Element und eine Liste eben eine Liste.

Wenn du verschiedene Treiber hast, dann leite die von einer Parentklasse 
ab die eine Methode init() hat.
Dann erzeuge dir Listen vom Typ der Parentklasse und du kannst 
unabhängig von der spezifischen Klasse des Treibers die Funktion init() 
aufrufen.

Ich bin gerade dabei ein geößerer Codeteil hier komplett neu zu 
designen, weil der ursprüngliche genau solche 'Abkürzungen' und 
'Vereinfachungen' im Design gemacht hatte und jetzt bei der 3. 
Erweiterung endgültig Schluss ist das noch dazuzufriemeln.
Also mach das was Karl Heinz sagt und designe es sauber.

von Uli T. (avaron)


Lesenswert?

Deswegen als Member-Variablen definieren und nicht als Basisklasse...
1
  class GenericDriver {
2
3
    static List<Type> statische_liste;
4
5
  }

somit kannste das in der speziellen Klasse als zweite, unabhängige Liste 
implentieren:
1
  class SpezializedDriver {
2
3
    static List<Type> statische_liste_spezialisiert;
4
5
  }

von Udo S. (urschmitt)


Lesenswert?

Uli Trautenberg schrieb:
> Deswegen als Member-Variablen definieren und nicht als Basisklasse...
>   class GenericDriver {
>
>     static List<Type> statische_liste;
> ...

Sorry aber für mich macht das wenig Sinn so.
Du willst eine Liste von Treibern..

jetzt definierst du eine Klasse Driver (Oder sogar Basisklasse weil das 
Teil "Generic" heisst) und in der Klasse Treiber hast du als Attribut 
die Liste. Weil du natürlich nicht in jeder Instanz eine eigene Liste 
willst machst du sie statisch.
Für mich ist das von hinten durch die Brust ins Auge.

Ist das irgendein Designpattern das ich nicht kenne?

von Oliver (Gast)


Lesenswert?

Udo Schmitt schrieb:
> Ist das irgendein Designpattern das ich nicht kenne?

Das Design-pattern nennt sich "Ham'werImmerSoGemacht", auch bekannt als 
"IstHaltSo,KannIchNixDrannÄndern".

Oliver

von Udo S. (urschmitt)


Lesenswert?

Oliver schrieb:
> Das Design-pattern nennt sich "Ham'werImmerSoGemacht", auch bekannt als
> "IstHaltSo,KannIchNixDrannÄndern".

:-)

Ach so ich glaube das heisst bei uns, 
deswoarvor20johrschungut,alsoissesjetztahnochguut.

Schöne Grüße

von Uli T. (avaron)


Lesenswert?

Das ist eher eine philosophie-Frage. Ich würde die Liste unabhängig von 
der eigentlichen Treiber-Klasse definieren, aber wenn sie schon 
unbedingt mit reinmuß, dann als statischen Member.

von Karl H. (kbuchegg)


Lesenswert?

Wenn man das Originalkonzept unbedingt und um jeden Preis halten muss, 
dann ginge auch noch:
Eine 2.te Liste machen, der Daten aus Pointern zu den Knoten in der 
ersten Liste besteht.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn man das Originalkonzept unbedingt und um jeden Preis halten muss,
> dann ginge auch noch:
> Eine 2.te Liste machen, der Daten aus Pointern zu den Knoten in der
> ersten Liste besteht.

Ich muss zurückrudern.
Ich glaube immer mehr, dass das ein schlechter Ansatz ist. Das Problem 
besteht darin, dass man dann ganz schnell mit einer 2.ten Liste dasteht, 
die Pointer auf Knoten enthält, die zb nicht mehr existieren.

Im Moment favorisiere ich eher einen anderen Ansatz:

Eine 2.te Listenklasse einführen, die von der ersten abgeleitet ist und 
die 2.te Verpointerung enthält. Und dann rigoros alle Memberfunktionen 
überschreiben, die die jeweilige Funktion aus der Basisklasse aufrufen 
und zusätzlich sich noch um die 2.te Verpointerung kümmern.

von Udo S. (urschmitt)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Eine 2.te Listenklasse einführen, die von der ersten abgeleitet ist und
> die 2.te Verpointerung enthält. Und dann rigoros alle Memberfunktionen
> überschreiben, die die jeweilige Funktion aus der Basisklasse aufrufen
> und zusätzlich sich noch um die 2.te Verpointerung kümmern.

Da die 'speziellen' Treiber aber auch in der 1. Liste weiter verbleiben 
sollen müsste er aber auch alle überschriebenen Funkionen der 
Parentklasse in seinen neuen Funktionen aufrufen.

von Karl H. (kbuchegg)


Lesenswert?

Udo Schmitt schrieb:

> Da die 'speziellen' Treiber aber auch in der 1. Liste weiter verbleiben
> sollen

Ach, ja richtig. Daran hab ich nicht mehr gedacht.
Er hat ja im Grunde eine heterogene Liste. Bei meinen Überlegungen hat 
sich jetzt im Kopf die Aufgabenstellung gedreht :-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.