int main(void)
{
vector<Dmxdaten *> cont;
vector<Dmxdaten *>::iterator it;
Dmxdaten *a_1 = new Dmxdaten("Ledwert",2,3,4);
cont.push_back(a_1);
for (it = cont.begin(); it != cont.end(); it++)
{ cout << *a_1; }
return 0;
}
Er wirft mir da immer bei cout die Adresse raus
mein Plan war folgender globale Operatorfuntion
ostream &operator<<(ostream &os, const vector<Dmxdaten*> &p)
{
cout << //Daten;
return os;
}
Leider gehts nicht er sprint nicht in die ostream
Hallo,
mit *a_1 bekommt du ja das object in dem container und das ist nunmal
ein zeiger.
cout << *a_1; sollte es tun.
Ich schreibe es immer so. Debuggt sich einfacher.
for (it = cont.begin(); it != cont.end(); it++)
{
Dmxdaten& tmp = *(*it);
cout << tmp;
}
Oh was soll überhaupt die Schleife mit dit Iterator wenn du ihn nicht
verwendest?
Das ist unsinn von mir
> mit *a_1 bekommt du ja das object in dem container und das ist nunmal> ein zeiger.> cout << *a_1; sollte es tun.
Peter schrieb:
> Oh was soll überhaupt die Schleife mit dit Iterator wenn du ihn nicht> verwendest?
Ich bin sicher das das wieder mal ein Tippfehler ist, der entstanden
ist, weil nicht tatsächlicher Code per Copy&Paste kopiert wurde, sondern
ein Bsp händisch hier eingetippt wurde.
>> Das ist unsinn von mir
Wenn mans wortwörtlich nimmt, hast du hier recht.
Wenn man die Frage gedanklich um die Tippfehler in der Fragestellung
bereinigt, ist deine Antwort goldrichtig.
und dann gehört hier kein ::iterator hin, sondern ein ::const_iterator,
und ++it ist bei Iteratoren immer besser als it++ (weil semantisch hier
egal, und besser optimierbar).
Wenn wir schon dabei sind, dann ist es schon mal (meistens) Blödsinn,
die Datenobjekte im vector dynamisch zu allokieren. Bringt nur
Mehraufwand, weil man sich dann auch um die Freigabe bzw. Kopie, op=
kümmern muss.
Was anderes ist es natürlich, wenn mehrere Referenzierungen auf
dieselben Daten notwendig sind. Aber das scheint hier nicht der Fall zu
sein.
-> Wann immer möglich, lass den std::vector sich um die eigentlichen
Objekte kümmern! Das ist sein Job!
Hans schrieb:
> ? ich hab mir das aus einem Buch abgeschrieben und da ist es so drinnen> aber ok
Wenn es in einem Buch steht, muß es ja noch nicht optimal sein.
@Karl heinz Buchegger
ja so sieht es erstmal besser aus. Hat aber sehr grosse Performance
nachteile. Jedesmal wenn ein object in den Vektor eingefügt wird, wird
von jeden Object der Copyconstructur aufgerufen und das Object kopiert.
Das mag bei einer Liste von 10O Objeken noch gehen, aber wenn mal ein
paar 1000 drin sind und das objekt selber wieder eine Liste hat. Dann
hat ist diese viel zu langsam.
Ich glaube sogar beim einfügen selber wird schon das erste Mal kopiert.
Peter schrieb:
> @Karl heinz Buchegger> ja so sieht es erstmal besser aus. Hat aber sehr grosse Performance> nachteile. Jedesmal wenn ein object in den Vektor eingefügt wird, wird> von jeden Object der Copyconstructur aufgerufen und das Object kopiert.> Das mag bei einer Liste von 10O Objeken noch gehen, aber wenn mal ein> paar 1000 drin sind und das objekt selber wieder eine Liste hat. Dann> hat ist diese viel zu langsam.> Ich glaube sogar beim einfügen selber wird schon das erste Mal kopiert.
Ja das wird es.
Kommt immer drauf an, wieviele Objekte es sind und wie teuer es ist eine
Kopie zu machen und wie häufig die Operation ist. Aber im Zweifel ist es
für einen Beginner vernünftiger als einen Haufen Speicherlecks zu
hinterlassen :-)
(Schade, dass die originalen Smart-Pointer nicht Containerfähig sind.
Aber wozu gibt es denn Boost :-)
Ich weiß ja nicht, wofür man hier einen std::vector braucht,
aber in 90% der Fälle ist der vector ohnehin die falsche Wahl.
Leider ist er einem Feld zu ähnlich, deshalb nimmt ihn jeder
für alles mögliche, anstatt einen passenden Container zu nehmen.
Das führt dann aber auch gleich wieder zu weit...
Also funktionieren tut es soweit (Ich erkäre es mal)
"Ledwert",------------------------------2,3,4
Ledewert wird von einer Klasse eingelesen die Werte 2,3,4 werden von
einer
abgeleiteten Klasse eingelesen nachdem das ganze dann eingelesen wurde
wird es in Vector oder list abgespeichert siehe oben
wieso ich das jetzt so gemacht habe ist doch klar
ich kann von der Oberklasse wieder ne neue Klasse ableiten
nur diesmal zb mit 10 Kanälen statt mit 3 wie jetzt
und kann dann mit Zeiger jedesmal zwischen den abgeleiteten klassen
wählen
das ist doch top
Ich habe es zwar nicht richtig verstanden, aber egal.
Es sieht zumindest so aus, daß in dem vector Zeiger auf
Objekte von verschiedenen Klassen liegen.
Das ist dann in der Tat ein Argument für Zeiger im vector
statt der Elemente selbst, anders geht es nicht.
Wenn eine std::list statt des vector auch möglich ist,
dann ist zumindest das Anfügen wesentlich effizienter.
Hans schrieb:
>> wieso ich das jetzt so gemacht habe ist doch klar>> ich kann von der Oberklasse wieder ne neue Klasse ableiten> nur diesmal zb mit 10 Kanälen statt mit 3 wie jetzt
Ähm. Das klingt jetzt nach einem falschen Einsatzzweck für
Polymorphismus.
Ob ein Lader 3 oder 10 Kanäle hat, ist eine Eigenschaft des Laders - ein
Property.
Wenn du eine Klasse public ableitest, dann entspricht das gemeinhin
einer 'Ist-ein' Beziehung.
So wie in:
Ein Hund ist ein Tier
class Tier
{
};
class Hund : public Tier
{
};
oder: Ein Pkw ist ein Auto
Wenn aber ein Lader 3 Kanäle hat und ein anderer hat in einer anderen
Ausbaustufe 10 Kanäle, dann ist das doch im Prinzip immer noch der
gleiche Lader, nur mit mehr Ladestufen. Er kann deswegen nicht mehr oder
weniger (zb. zusätzlich noch Kaffee kochen). Ich kann doch auch hergehen
und einen Lader von 3 Kanälen auf 10 Kanälen ausbauen, ohne dass sich am
Lader (abgesehen von der Anzahl der Ladestufen) etwas verändert. So wie
ein Pkw immer noch dasselbe Auto ist, wenn ich einen Kindersitz einbaue.
Was du hast ist am ehesten noch eine 'Hat-ein' Beziehung.
So wie in: Ein Auto hat einen Motor
Und das modelliert man mit einer Membervariablen
1
classAuto
2
{
3
...
4
private:
5
Motor*pMotor_;// Pointer, weil es verschiedene Motoren gibt
6
};
7
8
classPkw:publicAuto{};
9
classLkw:publicAuto{};
10
11
classMotor{};
12
classBenziner:publicMotor{};
13
classDiesel:publicMotor{};
Diese Gerüst modelliert:
Es gibt Autos
Es gibt Motoren
Ein Auto HAT_EINEN Motor
Ein Pkw IST_EIN Auto
Ein Lkw IST_EIN Auto
Ein Benzinmotor IST_EIN Motor
Ein Dieselmotor IST_EIN Motor
Und mit diesen Klassen kannst du jetzt alles zusammenstellen:
Ein Pkw mit Benzinmotor, Pkw mit Dieselmotor, Lkw mit Benzinmotor, Lkw
mit Dieselmotor.
Und wenn die Firma nächstes Jahr einen Elektromotor auf den Markt
bringt, ist das kein Problem. Dann gibt es eben einen Motortyp mehr:
1
classElektro:publicMotor{};
und schon kannst du alle Autos, sei es jetzt Pkw oder Lkw mit
Elektromotor ausrüsten. Denn: Ein Auto HAT_EINEN Motor.
In diesem Sinne HAT dein Lader 3 oder 10 Ladestufen, die vielleicht
verschiedene Ausprägungen haben können (was wies ich, mit
unterschiedlicher Technologie aufgebaut sind, wenn das für dein Programm
wichtig ist)
1
classLader
2
{
3
...
4
5
private:
6
std::list<Ladestufe*>ladeStufen_;
7
};
> und kann dann mit Zeiger jedesmal zwischen den abgeleiteten klassen> wählen
Da hast du recht: Sobald Polymorphie ins Spiel kommt, müssen es Zeiger
sein. Daran hab ich jetzt nicht gedacht.
Früher im studium wurde uns das so beigebracht
zb Fläche berechnen von Kreis und Dreieck
mann macht ne Base als Interface
Kreis und Dreieck leitet man ab
und speichert das ganze in nem Container ab
der container bekommt auch noch ne eigene Klasse
das ist auch mein Ziel für heute abend
Hans schrieb:
> Früher im studium wurde uns das so beigebracht>> zb Fläche berechnen von Kreis und Dreieck>> mann macht ne Base als Interface>> Kreis und Dreieck leitet man ab
Ja. Ist doch das gleiche Prinzip
Es gibt ein geometrisches Primitiv (= Basisklasse)
Ein Kreis IST_EIN geometrisches Primitiv
Ein Dreieck IST_EIN geometrisches Primitiv
Jedes geometrische Primitiv kann seine Fläche berechnen.
-> die Member Funktion area() kann grundsätzlich in die Basisklasse
wandern, da jedes geometrische Primitiv seine Fläche berechnen können
muss. Da aber die Flächenberechnung je nach tatsächlichem Primitiv
anders ausfällt, ist das eine virtuelle Funktion, die von den
tatsächlichen Primtiva implementiert werden muss.
Eine Basisklasse, die selbst abstrakt ist (von der man also kein Objekt
erzeugen kann) nennt man Interface. Wobei hier die Definition, wann man
genau von einem Interface spricht und wann von einer Basisklasse, etwas
schwammig ist. Normalerweise enthält ein Interface nur einen Satz von
abstrakten virtuellen Funktionen und nicht mehr.
> und speichert das ganze in nem Container ab> der container bekommt auch noch ne eigene Klasse
Das ist in dem Fall sicherlich sinnvoll, da der Container ja Pointer
enthalten muss, da Polymorphie gefordert ist. Wann immer ein Objekt
andere Resourcen dynamisch verwalten muss, ist man meistens gut beraten,
wenn man diese 'Details' in eine eigene Klasse verpackt. Das
zugrundeliegende Prinzip nennt man RAII (Resource Acquisition Is
Initialization)
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
list<string> liste;
list<string>::iterator iter;
liste.push_front("daten");
cout << liste.front();
wollte noch schnell was testen
kann ich nicht einen Datenstring in ne liste reinschreiben
und anschließend wieder auslesen?
Hans schrieb:
> list<string> liste;> list<string>::iterator iter;>> liste.push_front("daten");> cout << liste.front();>>> wollte noch schnell was testen>> kann ich nicht einen Datenstring in ne liste reinschreiben> und anschließend wieder auslesen?
sicher kannst du.
Eventuell solltest du deinem cout Objekt noch einen endl nachschieben,
damit es den Buffer ausleert.
int main ()
{
list<string> liste;
list<string>::iterator iter;
liste.push_front("daten");
cout << liste.front() << endl;
return 0;
}
so? das geht aber auch nicht!
Hans schrieb:
> int main ()> {> list<string> liste;> list<string>::iterator iter;>>> liste.push_front("daten");>> cout << liste.front() << endl;>>> return 0;> }>> so? das geht aber auch nicht!
Was geht nicht?
Wenn ich das hier
1
#include<iostream>
2
#include<list>
3
#include<string>
4
5
usingnamespacestd;
6
7
intmain()
8
{
9
list<string>liste;
10
list<string>::iteratoriter;
11
12
liste.push_front("daten");
13
cout<<liste.front();
14
cout<<endl;
15
}
durch den Compiler jage, compiliert alles wunderbar und das Ergebnis ist
wie erwartet.
Sag niemals "das geht nicht". Sag konkret, was das Problem ist. Du gehst
ja auch nicht zu deinem Arzt und sagst "Es tut weh"
Hallo ich hab mir mal noch ein paar gedanken gemacht
Ich hab das Programm
int main(void)
{
vector<Person **> cont;
vector<Person **>::iterator it;
Person *a_1 = new Person("Hans",2,3,4);
Person **a_2 = & a_1;
cont.push_back(a_2);
for (it = cont.begin(); it != cont.end(); it++)
{
cout << (***it);
}
return 0;
}
wieso springt der Combiler bei der Vektorhe hier rein
reference operator*() const
{// return designated object
return ((reference)**(_Mybase *)this);
}
Da dürfte ehr doch gar nicht reinspringen?
doch das ist eine überladenen operator vom iterator, das sollte soweit
stimmt. Aber warum zum Teufel sollte man einen Zeiger auf einen Zeiger
verwenden?
wo hast du reference operator*() const... her?
Aus dem vector-Header?
In std::vector ist ein Untertyp iterator definiert, den
du nimmst (entgegen meiner Empfehlung const_iterator zu nehmen,
so kann man sich den Mund fusslig reden...).
Und für den ist wiederum operator* überladen.
Mit *it rufst du genau dieses Ding auf.
Ich hab die abspeicherung der Daten nun so gelöst
Interface dann 2 Klassen die das Interface implementieren um
untersiedliche
Kanäle einzugeben und eine Klasse mit Vector als Container.
meine Problem ist folgendens Vector speicher ja nur Adressen von Ob ab.
das ist ja keine Problem solange die Ob existieren
aber wenn ich die Daten nun so einlesen will hab ich ein Problem
DatenKlasse1 daten_1;
DatenKlasse2 daten_2;
und das dann folgendes mache
for (int i = 0;i <10;i++)
{
cout << "Bitte Daten eingeben \n";
if (cin >> daten_1)
ch.gebein(&daten_1);
if (cin >> daten_2)
ch.gebein(&daten_2);
cin.clear();
cin.ignore(INT_MAX, '\n');
}
das ist natürlich nicht gerade schön mir ist schon klar das das so nicht
gehen kann aber wie löst man sowas in c++
Ich verstehe nicht was Du genau willst, kannst Du das bitte anders
formulieren?
Sind mit Ob Objekte gemeint?
Wo ist die Klasse mit den Vektoren?
Welche zwei Klassen haben welches Interface implementiert?
Du kannst schlecht erwarten, dass alle den ganzen Thread durchlesen,
damit sie Wissen was Du genau implementieren willst.
Hans schrieb:
>> das ist ja keine Problem solange die Ob existieren
Wenn sie nicht existieren, dann musst du sie erzeugen.
>> aber wenn ich die Daten nun so einlesen will hab ich ein Problem>> DatenKlasse1 daten_1;> DatenKlasse2 daten_2;
Das ist schon Käse.
Du weißt nicht, welche Objekte du brauchst, ehe dir der Benutzer nicht
sagt, was er haben möchte.
Ergo kannst du die Objekte so nicht anlegen, sondern musst abwarten bis
der Benutzer seine Wahl getroffen hat. Erst dann kannst du mittels new
das neue Objekt erzeugen, es mit Daten füllen und die Adresse des
Objektes in den vector stopfen.
Tja. Und jetzt geht der Eiertanz los.
Wer kümmert sich darum, dass die Objekte wieder freigegeben werden, wer
sorgt dafür, dass in einer Kopie des vectors auch Kopien der Objekte
sind, ditto für Zuweisung, etc.
(Bist du sicher, dass du auf der Uni schon mal C++ programmiert hast?
Deine Fragen sind sehr 'basic'. Ich kann kaum glauben, dass man auf der
Uni keine dynamische Objektallokierung in C++ mehr macht)
PS: Das
DatenKlasse1 daten_1;
DatenKlasse2 daten_2;
for (int i = 0;i <10;i++)
{
cout << "Bitte Daten eingeben \n";
if (cin >> daten_1)
ch.gebein(&daten_1);
if (cin >> daten_2)
ch.gebein(&daten_2);
cin.clear();
cin.ignore(INT_MAX, '\n');
}
ist höchst wahrscheinlich eine Todsünde. Stopfe nie die Adresse einer
Stack-Allokierten Variablen in einen Container, der Pointer verwaltet!
Der Container ist meistens Owner der Pointer, heißt: Er ist dafür
zuständig, dass die Objekte wieder delete-ed werden. Gibst du die
Adresse eines Stack-Allokierten Objektes in einen Container, dann macht
das einen kräftigen Bumm!
So macht man das:
Es ist nicht die for-Schleife, die dafür zuständig ist, dass ein Objekt
seine Daten bekommt. Im einfachsten Fall ist es das Objekt selber, denn
nur das Objekt weiß, welche Daten es benötigt.
Das folgt genau dem objektorientiertem Gendankengang: Funktionalität
dorthin wo sie hingehört, nämlich zum Objekt.
Wenn du die Datenobjekte von solchen Feinheiten, wie Ein/Ausgabe (die ja
des öfteren Formatabhängig sein wird) freihalten willst, dann baut man
eine Hilfsklasse, die über das konkrete Objekt und seine Bedürfnisse
Bescheid weiß, das Objekt erzeugt und es mit den Daten füttert.
1
#include<iostream>
2
#include<vector>
3
4
usingnamespacestd;
5
6
classPrimitiv
7
{
8
public:
9
virtualvoidreadData(){}
10
virtualvoidprintData(){}
11
};
12
13
classRectangle:publicPrimitiv
14
{
15
public:
16
virtualvoidreadData()
17
{cout<<"Rechteck:\n";
18
cout<<" Länge: ";cin>>length_;
19
cout<<" Breite: ";cin>>width_;
20
}
21
22
virtualvoidprintData()
23
{cout<<"Rechteck: Länge "<<length_<<" Breite "<<width_<<"\n";}
24
25
private:
26
doublelength_;
27
doublewidth_;
28
};
29
30
classCircle:publicPrimitiv
31
{
32
public:
33
virtualvoidreadData()
34
{cout<<"Kreis:\n";
35
cout<<" Radius: ";cin>>radius_;
36
}
37
38
virtualvoidprintData()
39
{cout<<"Kreis: Radius "<<radius_<<"\n";}
40
41
private:
42
doubleradius_;
43
};
44
45
intmain()
46
{
47
intchoice;
48
std::vector<Primitiv*>primitiva;
49
50
for(inti=0;i<5;++i)
51
{
52
Primitiv*newPrim=NULL;
53
54
cout<<"Was wollen sie? Rechteck(=0) oder Kreis(=1): ";
55
cin>>choice;
56
57
if(choice==0)
58
newPrim=newRectangle;
59
elseif(choice==1)
60
newPrim=newCircle;
61
62
if(newPrim)
63
{
64
newPrim->readData();
65
primitiva.push_back(newPrim);
66
}
67
}
68
69
cout<<"Sie haben eingegeben:\n";
70
for(size_ti=0;i<primitiva.size();++i)
71
primitiva[i]->printData();
72
73
for(size_ti=0;i<primitiva.size();++i)
74
deleteprimitiva[i];
75
}
Sinnvollerweise wird man natürlich den vector<Primitiv*> wieder in eine
eigene Klasse verpacken, die sich um das Datenmanagement kümmert, sodass
derjenige, der diesen Container benutzt sich darüber keine Gedanken
machen muss, dass Objekte richtig umkopiert bzw. gelöscht werden. Auch
wird man dieser neuen Containerklasse dann Methoden verpassen, die eine
Kopie eines Objektes erzeugen können (dazu benötigt man dann einen
virtuellen Copy Constructor) etc. Aber das würde jetzt zu weit führen.
Hallo an der FH wurden ja keine Projekte gemacht
sondern es waren Übungsaufgaben wo die jeweiligen Klassen
beschrieben und nur der jeweilige Code eingetragen werden musste
Informatik hab ich übrigens nicht studiert
Hans schrieb:
> Hallo an der FH wurden ja keine Projekte gemacht> sondern es waren Übungsaufgaben wo die jeweiligen Klassen> beschrieben und nur der jeweilige Code eingetragen werden musste
Oh Gott.
Jetzt gibt es diesen Fülltext-Schwachsinn auch schon in der
Programmierung. Vom Grundsatz "Wiederholung macht den Meister" hält man
anscheinend nicht mehr viel.
> Informatik hab ich übrigens nicht studiert
Gut. Ich nehm alles zurück. Bin davon ausgegangen, dass dein Studium
irgendwas mit Programmierung zu tun hatte.
natürlich hattem wir C++ als Prüfung
aber das waren magere 2 SWS Vorlesung und 2 SWS Übung
ich kann dein Programm was du da geschrieben hast schon nachvollziehen
aber selbst erstellen könnte ich es ohne vorgabe vom grundgerüst aus dem
stand heraus nicht.
Hans schrieb:
> natürlich hattem wir C++ als Prüfung> aber das waren magere 2 SWS Vorlesung und 2 SWS Übung> ich kann dein Programm was du da geschrieben hast schon nachvollziehen> aber selbst erstellen könnte ich es ohne vorgabe vom grundgerüst aus dem> stand heraus nicht.
Genau das meine ich mit 'Fülltext' und dessen Verdammung.
Das Meiste des Programms ist Standard-Routine-Kram. Wenn die Idee erst
mal da ist, wie ich das Zeugs angehe, dann kommt bei mir das
'Muskelgedächtnis' ins Spiel. Obiges tippen die Finger schon fast von
alleine, ohne dass ich groß darüber nachdenken muss. Nur bekommt man
kein 'Muskelgedächtnis' wenn man immer nur Lücken in einem Fülltext
aufüllt (aus genau dem gleichen Grund bin ich erbitterter Gegner der
'Mathe Hausaufgaben werden gemacht, indem man das Ergebnis auf den
kopierten Zettel in die Punktzeile einsetzt' Praxis unserer
Grundschulkinder)
Aber da kannst du nichts dafür. Da müsste man ein ernstes Wort mit den
Ausbildnern reden.