[C++] -> Brauche Funktionalität des übergeornd

Hi!

Ich habe folgendes Prob:

Ich Instanziiere Object B in Object A. Ich rufe in A ein Member von B auf benötige aber Eigenschaften und Funktionen in B die A hat. Bisher übergab ich mittels void * this und caste wieder auf mein Object. Mir gefällt das aber nicht. Wie kann man soetwas anderst lösen ? Alleine das es schon probs gibt beim deklarieren ist es denke ich sehr schlecht :frowning:

In A sind Eigenschaften die für jedes Object A vorhanden sind und in allen unterobjecten die von A verwaltet werden benötigt werden. Desweiteren stellt a einige Funktionen zur verfügung die b und alle anderen Unterobjecte nutzen können.

Beispiel:

class a
{
private:
Eigenschaften;
[…]
public:
string createPos(Object_Ground &amp:wink:;
string makeSource(…);
};

class b
{

[…]
public:
string createSource(void *);
}

string A::makeSource(…)
{
if(das und das)
b * temp;
[…]
b->createSource(this);
else if(das und das)
[…]
}

string b::createSource(void * Object);
{
a * temp = static_cast (Object);
auswerten = a->Eigenschaften;
Position = a->MakePosition(this);
return(fertigenSource);
}

Danke im vorraus

Hi,

Ich Instanziiere Object B in Object A. Ich rufe in A ein
Member von B auf benötige aber Eigenschaften und Funktionen in
B die A hat. Bisher übergab ich mittels void * this und caste
wieder auf mein Object. Mir gefällt das aber nicht. Wie kann
man soetwas anderst lösen ? Alleine das es schon probs gibt
beim deklarieren ist es denke ich sehr schlecht :frowning:

Deinen Beispielcode habe ich nicht geschnallt, da fehlt glaube ich was.
Du kannst Klassen auch deklarieren (ohne Definition). Beispiel:

class a;

class b {
public:
 void tuWas(a\* objekt);
};
class a {
public:
 void tuWasAnderes(b\* objekt);
};
void b::tuWas(a\* objekt) {objekt-\>tuWasAnderes(this);}
void a::tuWasAnderes(b\* objekt) {objekt-\>tuWas(this);}

void main(void) {
 a ah;
 b beh;
 beh.tuWas(&ah);
 ah.tuWasAnderes(&beh);
}

Ist das das, was Du wolltest (der Code ist natuerlich Quatsch, geht nur ums Prinzip)?

Gruss

Thorsten

P.S.:
Derartige Konstruktionen versucht man uebrigens in einem guten Design via Base-Pointer und Polymorphismus zu umgehen.

Nunja, grundsätzlich fällt mir da keine bessere ‚Methode‘ ein - du könnest daber den void-Zeiger und den cast loswerden:

  1. in der Deklaration vom class A, B als friend deklarieren (B hat dann auch private-Zugriff bei Objekten von A)

  2. bei der Funktion dann eine Referenz auf A übergeben

hth

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

P.S.:
Derartige Konstruktionen versucht man uebrigens in einem guten
Design via Base-Pointer und Polymorphismus zu umgehen.

Hallo Thorsten,

vielen Dank für die Info. Es hat mich schon weitergebracht.

Ich bin „Anfänger“ im Objektorientierten. Könntest Du mir bitte ein paar Informationen über o.g. geben ? Eventuell ein kleines Beispiel ? Ich bin mit der Konstruktion nicht zufrieden ich weiß aber nicht wie es besser geht. Aber Base-Pointer werde ich mal als schlagwort nehmen :smile:

Danke!

Micha

Hi,

Ich bin „Anfänger“ im Objektorientierten. Könntest Du mir
bitte ein paar Informationen über o.g. geben ? Eventuell ein
kleines Beispiel ? Ich bin mit der Konstruktion nicht
zufrieden ich weiß aber nicht wie es besser geht. Aber
Base-Pointer werde ich mal als schlagwort nehmen :smile:

Hm, schwer da ein Beispiel zu geben, weil ich Dein Probleme nicht genau kenne.
Und wenn Du Anfaenger bist waere s auch etwas viel was zu erklaeren ist, da wuerde ich eher ein gutes Buch empfehlen.

Ich kann ja mal ein paar Designmoeglichkeiten oberflaechlich beschreiben:
Du hast zwei Klassen, die gegenseitig Funktionalitaet voneinander benoetigen. Eine Moeglichkeit, das anzu gehen haben ich paesentiert. In diesem Vorschlag bleiben es zwei Klassen, die sonst nichts mitenander zu tun haben. Das kann in bestimmten Situationen Sinn machen.
Eine andere Moeglichkeit waere beide Klassen von einer Basisklasse abzuleiten. Die gemeinsam benoetigte Funktionalitaet kann dann in der Basisklasse zur Verfuegung gestellt werden und die Funktionen tuWas… koennen evtl. jeweils den gleichen Namen haben und virtuell sein (Polymorphismus). Aufgerufen werden die Funktionen dann nur noch (wenn Polymorphimus verwendet wird) ueber einen Pointer auf die Basisklasse, was viele Probleme trivial macht. Ein derartiges Design ist praktisch und elegant, sollte aber nur verwendet werden, wenn es wirklich Sinn macht (is a Beziehung!).
Eine dritte Moeglichkeit besteht darin, eine Klasse zu einem Member (oder gar nested member class) der anderen zu machen. In diesem Fall kann man gegebenenfalls dem Member einen Pointer auf die Umgebende Klasse geben (evtl auch friend verwenden) und hat dann zwei Objekte die recht intim miteinander umgehen :wink:
Mit sowas kann man z.B. das Interface einer Klasse sauberer organisieren oder Client-Call-Back Techniken verwirklichen.

So das war ein ziemlicher runumschlag durch Objektorientiertes Design und bringt Dir wahrscheinlich nicht viel. Ich will mal noch zwei Sachen betonen. Bei Objektorienter Programmierung ist Polymorphismus so wichtig wie das Konzept der Klasse und Design viel wichtiger als die ganze Syntax (allerdings ist eine Beherrschung der Syntax leider essentiel um zu wissen, welche Mittel einem fuers Design zur Verfuegung stehen).
Ich empfehle das Buch „Thinking in C++“ von Bruce Eckel, frei downloadbar unter http://www.bruceeckel.com/

Gruss

Thorsten

Hi Thorsten,

erst einmal vielen Dank für die Antwort.Hat schon neue einblicke in C++ gegeben :smile:

Das Buch habe ich mir gezogen, mal schaun wann sich die Zeit findet rein zu schauen.

Mein Problem ist folgendes:

Ich soll einen Druckertreiber (Thermotransferdrucker) schreiben, der Daten entgegen nimmt und sie in das Script des Druckers „übersetzt“.

Es gibt hier z.B. Textfelder und Barcodes und einer unvorhersehbaren Menge.

Die Treiber sollen für verschiedene Druckertypen eingesetzt werden. Also entsanden einige Klassen die das Grundbedürfniss
decken, die alle Drucker haben.

Davon habe ich wiederum eine Spezialisierung für Zebradrucker vorgenommen.

Ein Object - zebra_object_print - liest die Daten ein und ermittelt den Typ des Objektes das durch den Drucker gedruckt werden soll. (Hier hilft mal eine switch() weiter)

In einer Liste, die Zeiger des Grundobjektes aufnehmen kann (inkl. virtuelle Funktionen) erstellt die Objekte und füllt sie mit allem was man am Anfang benötigt.

So weit alles o.k.

Es gibt nun aber Daten die ich innerhalb des zebra_object_print global brauche, weil der Barcode z.B. wissen muß in welche richtung
der lezte BC gedruckt wurde, oder das Textfeld wissen muß, welche Sprache zu letzt gedruckt wurde, oder welche Trennzeichen zu
verwenden sind.

(Beim schreiben dieses Textes ist mir ein Designfehler aufgefallen - danke schon mal :smile: Kann noch was mit noch einer weiteren
Vererbung retten).

Das Grundobject hat z.B. die Routine virtul string createSource(void *) = 0;

Zebra_Object_Text enthält die gleiche Routine.

Zebra_Object_Text braucht noch Daten und Funktionen von Zebra_Object_Print um zu Funktionieren und da will ich möglichst sauber
rann. Wenn ich dem vererbten Object den void * durch Zebra_Object_Print & ersetze, mosert der Compiler rum, weil ich eine virtuelle
Funktion verdecke.

Vererben geht dabei nicht (denk ich mal) weil Zebra_Object_Print total unterschiedliche Objecte handelt.

Die Klassen in Zebra_Object_Print zu nehmen macht in meinen Augen keinen Sinn weil ich unterschiedliche Anzahl an BC oder
Textfeldern brauche.

Die lezte Möglichkeit (Da wo Du ein beispiel gemacht hast) geht (denk ich) um die casts los zu werden.

Könntest Du mir etwas dazu sagen, das der Compiler
class a; vor definition von der Klasse akzeptiert ?
Ist diese Variante Portabel ?

TNX schon mal für bisherige mühen

Hi,

Es gibt hier z.B. Textfelder und Barcodes und einer
unvorhersehbaren Menge.

Verwende dafuer am besten die STL container Klassen (vector, list, set, map, multimap …) wenn die entsprechenden Vorgaenge nicht arg geschwindigkeitskritisch sind (vector ist auch dann ok, ist recht schnell). Referenz unter http://www.dinkumware.com/htm_cpl/index.html

Ein Object - zebra_object_print - liest die Daten ein und
ermittelt den Typ des Objektes das durch den Drucker gedruckt
werden soll. (Hier hilft mal eine switch() weiter)

Kennst Du real time type identification?
Evtl. kann eine template Funktion den switch komplett ersetzen (evtl. auf kosten von Code Bloat, kommt drauf an).

Es gibt nun aber Daten die ich innerhalb des
zebra_object_print global brauche, weil der Barcode z.B.
wissen muß in welche richtung
der lezte BC gedruckt wurde, oder das Textfeld wissen muß,
welche Sprache zu letzt gedruckt wurde, oder welche
Trennzeichen zu
verwenden sind.

Das klingt so als koennten static members das Problem loesen (vermutlich in der Basisklasse?).

Zebra_Object_Text braucht noch Daten und Funktionen von
Zebra_Object_Print um zu Funktionieren und da will ich
möglichst sauber
rann. Wenn ich dem vererbten Object den void * durch
Zebra_Object_Print & ersetze, mosert der Compiler rum, weil
ich eine virtuelle
Funktion verdecke.

Hm. Evtl. hirarchy umdrehen und multiple inheritance verwenden. Der Tip ist aber mit grosser Vorsicht zu geniessen. MI kann tricky sein und Diamonds verursachen neben Technischen Feinheiten einigen Overhead.

Vererben geht dabei nicht (denk ich mal) weil
Zebra_Object_Print total unterschiedliche Objecte handelt.

Tja, kann ich nicht beurteilen.

Die Klassen in Zebra_Object_Print zu nehmen macht in meinen
Augen keinen Sinn weil ich unterschiedliche Anzahl an BC oder
Textfeldern brauche.

Versteh ich nicht. Laesst sich das nicht flexibel auf dem heap organisieren?

Die lezte Möglichkeit (Da wo Du ein beispiel gemacht hast)
geht (denk ich) um die casts los zu werden.

Dann ist das auf jeden Fall eine Option. Das muss auch nicht schlecht sein. Vielleicht ist es fuer Dein Problem das optimale Design.

Könntest Du mir etwas dazu sagen, das der Compiler
class a; vor definition von der Klasse akzeptiert ?

Naja, wie ich in meinem ersten Posting gezeigt habe. Erst deklarieren dann was anderes, dann definieren. Zwischen Deklaration und Definition akzeptiert der Compiler zwar nur Pointer, aber meist ist das voellig ok (statt per Value uebergibt man ohnehin besser per const reference, da eine referenz im Grunde eine Adresse ist koennte der Compiler das auch schlucken, bin mir aber nicht sicher). Zugriff auf members ist ausgeschlossen, meist aber auch nicht noetig, da die Definition der Funktionen ja spaeter erfolgen kann. Selbst inline Funktionen lassen sich mit stichwort inline ausserhalb des class bodies definieren (muessen dann im Header stehen).

Ist diese Variante Portabel ?

Gute Frage, naechste Frage :wink:
Theoretisch ist C++ mitlerweile voll standardisiert und alle Elemente inklusive STL voll portabel. Praktisch sind gewisse Sprachfeatures noch nicht oder nicht ueberall implementiert (namespace gibts z.B. glaube ich noch in kaum einem Compiler). Templates, RTTI und STL koennen Probleme verursachen. Ich habe da allerdings wenig Erfahrung. Die Variante mit erst Deklaration … spaeter Definition sollte imho portabel sein. Auf g++ funzt das alles und den gibts fuer die meisten wichtigen Systeme. Es waere also vielleicht eine gute Idee, einen Compiler wie g++ (gcc) zu verwenden, den es auf vielen Systemen gibt. Eine Garantie ist das zwar nicht, koennte aber helfen. Auf keinen Fall wuerde ich auf ein M$-Produkt setzen, wenn Du auf Portabilitaet wert legst.

Gruss

Thorsten

Hallo Thorsten,

vielen Dank für die vielen Infos.
Ich würde Dir gerne den Source einmal zusenden. Könntest Du mal ein Blick darauf werfen ? Ich habe noch das Problem das mit das als Windowscompilat abstürtzt. (Bis auf ReadJob sollte alles Portabel sein…) Als DOS Compilat klappt die sache. Ich habe mir noch kein Portablen ersatz für ReadJobFile gebaut. Eventuell siehst Du aber auf die schnelle einen groben Fehler.

Aber nicht die Hände über den Kopf zusammenschlagen :smile: Ich bin noch Anfänger (und muß wärend Du den Sorce ansiehst weiter arbeiten am Treiber).

Wäre aber dennoch lieb wenn Du mal schaust.

Kennst Du real time type identification?

Nö, kenn ich nicht. Aber hört sich aber Interesannt an. INPUT INPUT :smile:

TNX Micha

Hi,

vielen Dank für die vielen Infos.
Ich würde Dir gerne den Source einmal zusenden. Könntest Du
mal ein Blick darauf werfen ?

Wenn es open source ist ja, ansonsten vielleicht. Wieviel ist es denn? (Zeilen)

Kennst Du real time type identification?

Nö, kenn ich nicht. Aber hört sich aber Interesannt an.

Oops, da vertu ich mich immer wieder. Es heisst run time type identification. In C++ gibt es zwei Moeglichkeiten zur Laufzeit den Typ eines Obejektes zu bestimmen. Mit typeid(whatever) kriegst Du ein Object, dass einige Infos ueber den tatsaechlichen Typen des Objektes enthaelt. Damit kommst Du aber nur auf den tatsaechlichen Typen, wenn Du nachschauen willst, ob sich etwas auf irgendeine Ebene einer Hirarchy casten laesst brauchsst Du dynamic_cast(obejct).

Gruss

Thorsten

Hallöchen Thorsten

Wenn es open source ist ja, ansonsten vielleicht. Wieviel ist
es denn? (Zeilen)

Nein, ist leider kein Opensource und hat mittlerweile ein recht großes Umfang genommen. Eigentlich zu groß um zu schaun :wink:

Oops, da vertu ich mich immer wieder. Es heisst run
time type identification. In C++ gibt es zwei Moeglichkeiten
zur Laufzeit den Typ eines Obejektes zu bestimmen. Mit
typeid(whatever) kriegst Du ein Object, dass einige Infos
ueber den tatsaechlichen Typen des Objektes enthaelt. Damit
kommst Du aber nur auf den tatsaechlichen Typen, wenn Du
nachschauen willst, ob sich etwas auf irgendeine Ebene einer
Hirarchy casten laesst brauchsst Du
dynamic_cast(obejct).

TNX für die Info. Den Typ des Objectes muß ich vor dem erstellen erkennen. Dazu legt mir jemand in einer IniDatei einen String ab. Über ein Object wird dieser ausgewertet und via einer ID und switch weiß ich dann welchen Typ der Anwender in der Ini liegen hat.

Ich möchte mich bei Dir für Deine Hilfe bedanken. Es hat einige Kentnisse in C++ gebracht die ich noch ausweiten werde. Bin nun einige Kniffs reicher :smile:

Daaaaaaaannnnnkkkkeeeeee!!!

Michael

Hi,

Nein, ist leider kein Opensource und hat mittlerweile ein
recht großes Umfang genommen. Eigentlich zu groß um zu schaun
:wink:

Kommt drauf an, wie gut das Design ist :smile:
Wieviel Zeilen sind es denn. Und wieviele Klassen c.a.?

Gruss

Thorsten

Hi,

Nein, ist leider kein Opensource und hat mittlerweile ein
recht großes Umfang genommen. Eigentlich zu groß um zu schaun
:wink:

Kommt drauf an, wie gut das Design ist :smile:
Wieviel Zeilen sind es denn. Und wieviele Klassen c.a.?

Etwa 30 Klassen wovon vllt. 7 von einander abhängig sind.
Zeilen… 10000 ? :smile:

Das Design ist bestimmt schlecht - weil Anfänger in C++. Ich habe zwar zuvor mal eine minni klasse geschrieben, aber die machte nicht viel. Ich schick es Dir einfach mal. (Leider ist eine Klasse spetziell auf ein Borland Object ausgelegt :-/ ).

Wenn Du nicht gleich die Spucktüte brauchst, bin ich schon mal froh :smile:

TNX Micha

Gruss

Thorsten

Hi!

Hab mir mal das alles näher angeschaut.
Das Design ist nun total geändert.

Alle sachen die nicht direkt mit dem Objekt zu tun hatten hab ich nun nach ausen in den globalen raum gelegt (mit namespace).

Beispiel:
Zeb_Globale_Sachen()
{

}

Zeb_Globale_Sachen * Glob = NULL;

Im Konstruktor des obersten Objectes steht dann folgendes:

if(Flob == NULL)
Glob = new Zeb_Globale_Sachen;

Die frage ist nun an Dich, ob das zuverlässig (portabel) ist, oder ob der Compiler nicht zur initialisierung verpflichtet ist. Ich will den Globalen krempel nicht auf dem Stack liegen haben da ich da eh zu knapp bin mit.

TNX

Micha