Forum: PC-Programmierung C++ und this


von Franziskaner (Gast)


Lesenswert?

Wieso geht das nur so?

Ist ne eigene Klasse!!!!!!!!!!!!!

String aaa = *this;          //das geht
  cout << aaa.pg;         //das geht

   cout <<*this.pg; //geht nichtttttttttttttttttttttttttttt

cout << this->pg; //das geht

wieso nur?

wieso geht das nicht *this.pg;

von (prx) A. K. (prx)


Lesenswert?

Falsche operator precedence.
*a.b entspricht *(a.b), nicht etwa (*a).b.

von Franziskaner (Gast)


Lesenswert?

aha

und wenn ich zb das hier aufrufe

operator=(const String &sdd)

bla bla


mit s1 = s2

dann ist s1 der this teil und s2 sdd

von Franziskaner (Gast)


Lesenswert?

das bedeutet dann ja eigentlich


das aus der Funktion operator=(const String &sdd)

das hier gemacht wird operator=(String *this; const String &sdd)

kann man sich das so ungefär vorstellen

von Karl H. (kbuchegg)


Lesenswert?

Franziskaner schrieb:
> das bedeutet dann ja eigentlich
>
>
> das aus der Funktion operator=(const String &sdd)
>
> das hier gemacht wird operator=(String *this; const String &sdd)
>
> kann man sich das so ungefär vorstellen


Ja, so in etwa.
Eine Memberfunktion einer Klasse ist intern eine ganz normale Funktion, 
der ein Zeiger auf das Objekt für welches es aufgerufen wird, mitgegeben 
wird.

aus
1
class A
2
{
3
  public:
4
    void foo( int j )
5
    { i = j; }
6
7
  protected:
8
    int i;
9
};
10
11
int main()
12
{
13
  A a;
14
15
  a.foo( 5 );
16
}

macht der Compiler konzeptionell für sich
1
struct A
2
{
3
  int i;
4
};
5
6
void A_foo( struct A* this, int j )
7
{
8
  this->i = j;
9
}
10
11
int main()
12
{
13
  struct a;
14
15
  A_foo( &a, 5 );
16
}

Ein Operator ist im Grunde auch nur eine normale Memberfunktion. 
Lediglich der Aufruf sieht im Code anders aus.

In der Tat haben die ersten C++ Compiler genau so gearbeitet: Die haben 
den C++ Code nach so einem Schema in C übersetzt und dann den ganz 
normalen C Compiler benutzt. In der Zwischenzeit hat sich aber C++ von C 
in einigen Bereichen schon zu weit entfernt, so dass das heute nicht 
mehr gehen würde.

von Franziskaner (Gast)


Lesenswert?

hab nämlich das Problem das 2 Objekte miteinander verglichen werden 
müssen
und da wird dann ne referenz mit nem zeiger verglichen so wie ich das 
sehe


if (this == &a)

bla bla


return *this oder a;


bei a wird da ne referenz übergeben und bei this dann der Zeiger

von Karl H. (kbuchegg)


Lesenswert?

Franziskaner schrieb:
> hab nämlich das Problem das 2 Objekte miteinander verglichen werden
> müssen
> und da wird dann ne referenz mit nem zeiger verglichen so wie ich das
> sehe
>
>
> if (this == &a)
>
> bla bla
>
>
> return *this oder a;
>
>
> bei a wird da ne referenz übergeben und bei this dann der Zeiger

Basics!

this ist ein Zeiger, das ist korrekt.

Aber &a kann keine Referenz sein. Referenzen tauchen immer nur bei 
Deklarationen auf. Ansonsten merkst du nie irgendetwas von einer 
Referenz!

& ist hier der Adress-Of Operator! Er liefert die Speicheradresse von a

von Franziskaner (Gast)


Lesenswert?

doch kann

operator =  (const Daten &a)

if (this == &a)  denke mal das  da die Adressen verglichen werden

alternativ gehen auch die Werte

this->daten == a.daten


alsi die neuen Daten sollen in die alten Daten geschrieben werden wenn 
ungleich

hab ich schon getestet

von Karl H. (kbuchegg)


Lesenswert?

Franziskaner schrieb:
> doch kann

nein, kann nicht

>
> operator =  (const Daten &a)

Das ist eine Referenz ...

>
> if (this == &a)  denke mal das  da die Adressen verglichen werden

... und hier hat das Zeichen & die Bedeutung des AdressOf Operators.


Das Zeichen & hat unterschiedliche Bedeutungen, je nachdem wo es 
verwendet wird.

>
> alternativ gehen auch die Werte

Auch das ist falsch.


> alsi die neuen Daten sollen in die alten Daten geschrieben werden wenn
> ungleich

Spar dir den Vergleich und machs richtig. Wenn in einem op= gleich am 
Anfang der Vergleich vorkommt
1
T& T::operator=( const T& rhs )
2
{
3
  if( &rhs != this ) {
4
    ...
5
  }
6
7
  return *this;
8
}

dann hat dieser Operator ein Problem. In einem korrekt geschriebenem op= 
benötigt man diesen Vergleich nicht.


>
> hab ich schon getestet

Was du getestet hast, weiß ich nicht. Aber dein C++ Verständnis ist 
lausig.

von Franziskaner (Gast)


Lesenswert?

Da soll der Zuweisungskon. getestet werden
und bei daten1 = daten1 soll er nichts machen

nur bei daten1 = daten2 soll er kopieren

von Franziskaner (Gast)


Lesenswert?

Das ist das Beispiel einen Professors das ist sein Skript
ich glaub nicht das er misst schreibt

von Karl H. (kbuchegg)


Lesenswert?

Franziskaner schrieb:
> Das ist das Beispiel einen Professors das ist sein Skript
> ich glaub nicht das er misst schreibt

Oh. Das sind die schlimmsten.
Sag ihm einen schönen Gruss von mir und er soll mal einen C++ Kurs 
belegen.

von Franziskaner (Gast)


Lesenswert?

Ja sorry war verschrieben!!!!!!!!!!!!!!!!!!!!!

von Franziskaner (Gast)


Lesenswert?

Aber du vergleichst doch da


die adressen oder was sonst

if( &rhs != this )

von Karl H. (kbuchegg)


Lesenswert?

Franziskaner schrieb:
> Da soll der Zuweisungskon. getestet werden
> und bei daten1 = daten1 soll er nichts machen
>
> nur bei daten1 = daten2 soll er kopieren

Aha.
Mal nur so nebenbei. Was denkst du. Wie oft kommt wohl in einem 
durschschnittlichen Programm die erste Zeile vor? Von 10Millionenn 
aufrufen sind maximal 2 oder 3 dabei, bei denen das tatsächlich 
passiert. Das bedeutet aber im Umkehrschluss, du machst 9 Millionen 900 
Tausend und ein paar Zerquetsche Vergleiche völlig umsonst (denn du 
musst ja dann sowieso zuweisen) nur damit du bei den 2 oder 3 
Zuweisungen, bei denen du dir die Zuweisung hättest sparen können etwas 
Zeit einsparst.

Wow: Super Optimierung!

Auch Vergleiche kosten Zeit!

von Karl H. (kbuchegg)


Lesenswert?

Franziskaner schrieb:
> Aber du vergleichst doch da
>
>
> die adressen oder was sonst
>
> if( &rhs != this )

Ganz genau.
Und wenn du sowas in einem op= siehst, weißt du sofort, dass das ein C++ 
Anfänger geschrieben hat, der von Exception-Safe programmieren keine 
Ahnung hat. Ein op=, der exception safe ist, benötigt diese Abfrage 
nicht. Und als Optimierung taugt es ebensowenig, weil das bischen 
Zeiteinsparung bei der Zuweisung locker durch die zusätzliche Abfragen 
bei den Zuweisungen bei denen tatsächlich zugewiesen werden muss, 
aufgefressen wird.

von Franziskaner (Gast)


Lesenswert?

wenn ich dir jetzt noch schreib das wir die Kopie mit strcpy (war wo 
anders strlen)
gemacht haben (natürlich nur wenns Unterschiedlich ist) und der Combiler
davor schon von sich aus warnt dann wirste warscheinlich...

von Karl H. (kbuchegg)


Lesenswert?

Alles klar. ihr macht dynamisches Resourcenmanagement

Gegen strcpy an sich ist nichts einzuwenden. Aber in einem op= muss man 
das ganze richtig machen.

Die falsche (vermeintlich richtige) Version ist
1
String& String::operator=( const String & rhs )
2
{
3
  if( &rhs != this ) {
4
    delete [] pointer;
5
    pointer = new char [ strlen( rhs.pointer ) + 1 ];
6
    strcpy( pointer, rhs.pointer );
7
  }
8
9
  return *this;
10
}

die korrekte Version ist
1
String& String::operator=( const String & rhs )
2
{
3
  char * tmp = new char [ strlen( rhs.pointer ) + 1 ];
4
  strcpy( tmp, rhs.pointer );
5
  delete [] pointer;
6
  pointer = tmp:
7
8
  return *this;
9
}

Der Unterschied:
denk mal darüber nach, was wohl in Version 1 mit dem Objekt passiert, 
wenn new eine Exception wirft und keinen Speicher allokieren kann. Zu 
dem Zeitpunkt ist der delete [] pointer schon durch und das String 
Objekt steht ohne gültige Speicherfläche da.
In Version 2 kann das nicht passieren. Der delete geschieht erst nachdem 
der neue Speicher allokiert wurde.

von (prx) A. K. (prx)


Lesenswert?

Karl heinz Buchegger schrieb:

> Spar dir den Vergleich und machs richtig. Wenn in einem op= gleich am
> Anfang der Vergleich vorkommt ... dann hat dieser Operator ein Problem. > In 
einem korrekt geschriebenem op= benötigt man diesen Vergleich nicht.

Würde ich in dieser apodiktischen Form nicht unterschreiben.

Ich kann mir durchaus Fälle vorstellen, in denen man damit besser fährt 
oder garnicht darum herum kommt. Beispielsweise weil man inkonsistente 
oder Information verlierende Zwischenzustände kriegen kann, insbesondere 
wenn private base classes dabei sind. Oder wenn man um das bei Identität 
zu vermeiden um's Eck herum über temporaries arbeiten muss, und dann 
dreht sich deine Statistik ins Gegenteil um, weil man das in 99% der 
Fälle eigentlich nicht braucht.

von Franziskaner (Gast)


Lesenswert?

Bei uns im Skript steht das !!!!!!!!!!!!!!

 if( &rhs != this ) {
    delete [] pointer;
    pointer = new char [ strlen( rhs ) + 1 ];
    strcpy( pointer, rhs.pointer );
  }

  return *this;
}

genau so lol

aber deine Version ist besser das ist klar

wir haben halt die String klasse nachprogrammiert

und die meisten dachten so wie ich das man die abfrage machen muss
steht ja auch im sktript

von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:

> dreht sich deine Statistik ins Gegenteil um, weil man das in 99% der
> Fälle eigentlich nicht braucht.

Den Vergleich benötigt man im wesentlichen eigentlich nur wenn 
dynamische Allokierungen im Spiel sind. In den anderen Fällen mag es 
Fälle geben, in denen tatsächlich ein Speedup möglich ist und die 
Kopieraktion im Vergleich zur Abfrage unendlich teuerer ist. Zb. wenn 
komplette Datenbanken kopiert werden.

Bei dynamischen Resourcen ist die Reihenfolge immer:
* zuerst die neuen Resourcen allokieren
* dann umkopieren
* dann die vorhandenen Klassendaten zerstören
* die neu allokierten Pointer an die Membervariablen zuweisen.

Hält man sich nicht an diese Reihenfolge, kommt man unweigerlich in 
Probleme, sobald ein new (und der ist das eigentliche Problemkind) eine 
Exception wirft. Der delete der bisherigen Resource muss solange als 
möglich hinausgezögert werden. Sobald die delete durchlaufen, darf es 
keine Möglichkeit für eine Exception mehr geben. (Gegebenenfalls muss 
man sie im op= mit einem catch Block abfangen und am Ende einen rethrow 
machen. Aber oberstes Ziel muss es immer sein, die Klasse in einem 
konsistenten Zustand zu halten)

Basisklassen sind kein Problem. Deren Aufruf des op= kommt ganz an den 
Anfang des op=, sodass die restliche Zuweisungssequenz nur dann 
durchlaufen wird, wenn auch die Basisklasse korrekt zugewiesen werden 
konnte. Wirft die Basisklasse eine Exception, darf auch der eigene op= 
nicht gemacht werden.

von Franziskaner (Gast)


Lesenswert?

die Frage ist wie macht es die Klasse String
letztändlich möchte idiese Klasse für zukünftige Programme verwenden

von Karl H. (kbuchegg)


Lesenswert?

Franziskaner schrieb:
> die Frage ist wie macht es die Klasse String
> letztändlich möchte idiese Klasse für zukünftige Programme verwenden

Solange du nicht weißt, was Exceptions sind, kannst du es auf beide 
Arten machen. Sobald ihr aber Exceptions durch habt, sollte eure Form 
für dich tabu werden - sie ist nicht Exception-safe.

von Franziskaner (Gast)


Lesenswert?

Ich weis was Exceptions sind ich lerne für die künftige c++ Prüfung
so komplette Projekte haben wir nicht gemacht es dreht sich halt nunmal
um "Basics" immerhin haben wir nen kopletten container per hand 
nachprogrammiert aber da war soviel c drinnen mit pop und front usw
das die meisten jetzt bei c++ hängen  und komplexe c++ Problem gar nicht
erkennen weil man vor lauter c nicht c++ sieht

von Karl H. (kbuchegg)


Lesenswert?

Franziskaner schrieb:
> Ich weis was Exceptions

Gut.
Dann musst du dir immer vor Augen halten, dass ein new eine Exception 
werfen kann.

Wenn hier
1
 if( &rhs != this ) {
2
    delete [] pointer;
3
    pointer = new char [ strlen( rhs ) + 1 ];
eine Exception auftritt, dann hat dein String Objekt seine bisherigen 
Daten schon über Bord geworfen. Und damit zeigt pointer auf Speicher, 
der nicht mehr dem Objekt gehört. Der op= wird aufgrund der Exception 
verlassen und der nächste Aufruf einer Memberfunktion von String, die 
pointer benutzt, macht dann BUMM!

Ich weiß, man sieht diese Form häufig. Das macht sie aber nicht 
richtiger.

von Franziskaner (Gast)


Lesenswert?

soweit haben wir das nicht getrieben
wir haben eine
try....


und einen catch




block in der main definiert


bei catch int //gib das aus
bei catch double //gib was  anderes aus

und wenn zb mal eine Medtode einer Klasse nen bestimmten
Zählerstand hatte dann wurde mit throw irgendwas ausgelöste

das ist jetzt nicht so komplizeirt

von Karl H. (kbuchegg)


Lesenswert?

Franziskaner schrieb:
>
> bei catch int //gib das aus
> bei catch double //gib was  anderes aus
>
> und wenn zb mal eine Medtode einer Klasse nen bestimmten
> Zählerstand hatte dann wurde mit throw irgendwas ausgelöste

Ich hoffe mal, dieser Zählerstand war stellvertretend für einen 
Fehlerfall.
Exceptions sind eigentich dafür gedacht, Fehler an den Aufrufer zu 
übermitteln. Wobei die reine Lehre da auch noch unterscheidet zwischen 
Fehlern, mit denen ein Aufrufer rechnen muss und Fehlern mit denen er 
nicht rechnen muss.
Wird eine Datenbank nach einem bestimmten Eintrag durchsucht, dann muss 
der Aufrufer damit rechnen, dass der Eintrag nicht gefunden wird. In so 
einem Fall ist eine Exception nicht angebracht und das Nichtfinden wird 
über ganz normle Kanäle (zb durch einen NULL Pointer als Return Wert) 
angezeigt.
Greift man aber auf eine Datei zu, dann muss der Aufrufer nicht damit 
rechnen, dass just in diesem Moment jemand die CD aus dem Laufwerk 
rausfährt.

Allerdings sind die Übergänge hier fliessend.

Und ja: es gibt auch die andere Schule, die auch normale Ergebnisse 
anstelle von Return Werten über Exceptions zurückgibt.

Meine persönliche Meinung, die ich aber niemandem aufdränge: Im letzten 
Fall werden Exceptions zu etwas vergewaltigt, wofür sie nie gedacht 
waren. Aber das ist IMHO.

von Franziskaner (Gast)


Lesenswert?

bei uns gabs ne aufgabe biergarten und wenn der voll war wurde auch eine 
Exceptions ausgelöst cout << " Biergarten voll" füe falscheingaben zb in 
statt double
wurde auch eine Exceptions ausgelöst

von Karl H. (kbuchegg)


Lesenswert?

Franziskaner schrieb:
> bei uns gabs ne aufgabe biergarten und wenn der voll war wurde auch eine
> Exceptions ausgelöst cout << " Biergarten voll"

Ist Grenzwertig.
Meine Ansicht: Würd ich nicht machen.
Wenn ich in einen limitierten Container was einfüge, und im Container 
ist kein Platz, dann ist das ein Ereignis mit dem ich als Aufrufer 
rechnen muss. Ist aber tatsächlich Ansichtssache, ob ich als Aufrufer da 
einen Returncode (true, false) angenehmer finde, oder doch eine 
Exception bevorzuge.

> füe falscheingaben zb in
> statt double
> wurde auch eine Exceptions ausgelöst

Das finde ich soweit ok, auch wenn ein Programmteil immer mit einer 
Fehleingabe rechnen muss.

von Franziskaner (Gast)


Lesenswert?

meine hauptprobleme waren zeiger und referenzen mit diesem this denke 
ich mal
hab ich das abgeschlossen
das ist im übrigen das größte Problem bei uns Studenten gewesen

Vererbung
Inteface
friend
stream klassen
find ich jetz
auf unsere "kleinen" Projekten bezogen nicht so schwer

morgen mach jetzt nochmal über die Templatets

und über die Containerklassen her
das sollte man am besten auswendig wissen wie man da sucht und einfügt
und bei doppelten einträgen nicht macht usw

von Klaus W. (mfgkw)


Lesenswert?

Biergarten voll ist ein grundlegender Fehler, keine Frage.
Damit muß und kann man nicht rechnen, ein Ausnahmezustand ohne
jeden Zweifel.

Im Übrigen hat KHB vollkommen recht damit, daß das delete nicht
stattfinden darf, solange der Rest noch schief gehen kann.
Ob man trotzdem vorher einen Test macht, ob an sich selbst zugewiesen
werden soll, ist aber davon unabhängig. Ich würde es in der Regel
nicht machen (eine solche Kopie ist unsinnig, und wenn sie unnötig
lange dauert, ist der Aufrufer selber schuld - zu blöd halt).
Wenn sein Prof es so machen will, warum nicht? Tut ja nicht weh,
zumindest nicht übermäßig.

von Karl H. (kbuchegg)


Lesenswert?

Klaus Wachtler schrieb:

> Ob man trotzdem vorher einen Test macht, ob an sich selbst zugewiesen
> werden soll, ist aber davon unabhängig.

Right

> Ich würde es in der Regel
> nicht machen (eine solche Kopie ist unsinnig, und wenn sie unnötig
> lange dauert, ist der Aufrufer selber schuld - zu blöd halt).
> Wenn sein Prof es so machen will, warum nicht? Tut ja nicht weh,
> zumindest nicht übermäßig.

Das Problem, das ich sehe ist, das dieser Vergleich als unbedingt 
notwendig gelehrt wird. Ich selbst habs auch so gelernt, bis ich dann in 
diversen Newsgroups von echten Profis (Herb Suttner, Jusuttis, Francis 
Glasborrow, etc) gelernt habe, wo der Schuh bei dieser Lösung drückt. 
Schade, dass Herb seine regelmässige "Guru of the week" Kolumne in 
comp.lang.c++.moderated schon lange aufgegeben hat. Man konnte so 
unendlich viel von ihm lernen.

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.