Kruze Frage zur Ermittlung der String-Länge
-
Hier einmal die Musterlösung einer Klausuraufgabe
void Konto::set_kontoinhaber(const char* kontoinhaber) { // Ermittlung der Stringlänge int i=0; while(kontoinhaber[i++]!='\0'); // Speicherfreigabe und -anforderung delete[] this->kontoinhaber; this->kontoinhaber = new char[i]; // Zeichenweises Kopieren for(int j=0; j<i; j++) { this->kontoinhaber[j]=kontoinhaber[j]; } }Die Speicherfreigabe und und die dynamische Anforderung von neuem Speicher sowie das zeichenweise Kopieren verstehe ich, auch wenn ich ehrlicherweise nicht drauf gekommen bin.
Aber was ist die Bedeutung von
int i=0; while(kontoinhaber[i++]!='\0');So eine while (Schleife?) habe ich noch nie gesehen, ich dachte immer die gäbs immer nur kopf oder fußgesteuert und da stehen dann Bedingungen und in den geschweiften Klammern Anweisungen.
Dieses "Ding" hier scheint aber nur aus einer Bedingung zu bestehen, die aber komischerweise mit einem Semikolon abschliesst.Es wäre nett wenn mir mal jemand die Bedeutung davon sagen könnte, vor allem in Bezug auf den dann folgenden Code des Beispiels.
Meine Gedanken:
Solange die Bedingung in einer while schleife true ist, werden die Anweisungen durchgeführt. Hier gibts aber gar keine Schleife, und keine Anweisungen. Wieso sollte er da überhaupt mehr als einmal durchgehen und i mehr als einmal inkrementieren. Raff ich nicht.Danke für Eure Antworten
Gruß
-
Naja, diesee Schleife tut auch wirklich nichts, da kein Block mit {} kommt, sonder n einfach ein Semikolon. Da jedoch i durch das Inkrement (i++) hochgezählt wird, ist die Aussage in der While-Bedingung irgendwann falsch.
Verständlich wäre, wenn man es so geschrieben hätte:
while(kontoinhaber[i] != '\0') { i++; }Aber wie du siehst spart man sich durch die andere Schreibweise 2 Zeilen und es wirkt einfach sehr elegant.
-
Musterlösung ???
brrr.....
-
Die "Musterlösung" ist auch nicht exception-safe.
Besser wäre so:void Konto::set_kontoinhaber(const char* kontoinhaber) { // Ermittlung der Stringlänge int i=0; while(kontoinhaber[i++]!='\0'); // Speicherfreigabe und -anforderung char* temp = new char[i]; delete[] kontoinhaber; kontoinhaber = temp; // Zeichenweises Kopieren for(int j=0; j<i; j++) kontoinhaber[j]=kontoinhaber[j]; }
-
Die Musterlösung währe std::string zu verwenden um nicht das Rad neu zu erfinden.
-
Danke für die Antworten. Als Anfänger wäre der ausgeschriebene Code wesentlich leichter zu verstehen, aber jetzt verstehe ich was gemeint ist und wie gezählt wird. Toll daß so Abkürzungen niemals erklärt werden und man immer erst grübeln muss...
wonderer schrieb:
Die Musterlösung währe std::string zu verwenden um nicht das Rad neu zu erfinden.
Nun ja, das ist ja gerade der Gag an dieser Klausur-Aufgabe: Wir sollen es selber machen.
Gruß
-
was ich nicht verstehe ist folgende zeile
delete[] kontoinhaber;"kontoinhaber" ist doch ein "const char*", kann also nicht verändert werden, aber hier wird bewusst der speicherbereich frei gegeben. Müsste es da nicht bei der Übersetzung einen Fehler geben?
Edit: doch jetzt sehe ich, dass es doch nur ein fehler war, in der Musterlösung sieht es besser aus.
-
hustbaer schrieb:
Die "Musterlösung" ist auch nicht exception-safe.
Besser wäre so:void Konto::set_kontoinhaber(const char* kontoinhaber) { // Ermittlung der Stringlänge int i=0; while(kontoinhaber[i++]!='\0'); // Speicherfreigabe und -anforderung char* temp = new char[i]; delete[] kontoinhaber; kontoinhaber = temp; // Zeichenweises Kopieren for(int j=0; j<i; j++) kontoinhaber[j]=kontoinhaber[j]; }Ich glaube die this - Zeiger hatten schon ihre Berechtigung, es scheint, als gäbe es in der Klasse Konto auch eine "kontoinhaber" als Member. Nun ja, eine bessere Benennung der Variablen wäre schon nicht schlecht gewesen^^.
-
wozu gibs denn this? finds schon ok, für getter/setter parameter dieselben namen zu verwenden, wie für die entsprechenden member.
und die änderung von hustbaer trägt auch nix zur exception safety bei. das war der code schon vorher, da es schlicht nix gibt, was man in diesem minicode in hinsicht darauf beachten müsste.
-
thordk schrieb:
wozu gibs denn this? finds schon ok, für getter/setter parameter dieselben namen zu verwenden, wie für die entsprechenden member.
Ok, das mit den Variablen-Namen habe ich übersehen.
Davon abgesehen finde ich es grausig this-> zu schreiben. "this" gibts für Fälle wo man den "this" Zeiger irgendwo übergeben muss, also für Dinge wie "ptr->Foo(this)". IMO sicher NICHT dafür dass man zwischen Membern und nicht Membern mit gleichem Namen unterscheiden kann. BTW: wenn man "m_" für Member verwendet hat man das Problem auch gleich garnicht.thordk schrieb:
und die änderung von hustbaer trägt auch nix zur exception safety bei. das war der code schon vorher, da es schlicht nix gibt, was man in diesem minicode in hinsicht darauf beachten müsste.
Blödsinn. Original:
void Konto::set_kontoinhaber(const char* kontoinhaber) { // Ermittlung der Stringlänge int i=0; while(kontoinhaber[i++]!='\0'); // Speicherfreigabe und -anforderung delete[] this->kontoinhaber; // alten Speicher freigeben, Zeiger zeigt aber noch auf den freigegebenen Speicher this->kontoinhaber = new char[i]; // hier fliegt ein bad_alloc - *bumm* du bist tot // Zeichenweises Kopieren for(int j=0; j<i; j++) { this->kontoinhaber[j]=kontoinhaber[j]; } }Die "richtige" Version sieht dann so aus (diesmal mit passenden Variablen Namen):
void Konto::set_kontoinhaber(const char* name) { // Ermittlung der Stringlänge int i=0; while(name[i++]!='\0'); // Speicherfreigabe und -anforderung char* temp = new char[i]; // neuen Speicher anfordern, hier kann ein bad_alloc fliegen, aber es wurde noch nichts verändert delete[] kontoinhaber; // alten Speicher freigeben, kann nix werfen kontoinhaber = temp; // variable umsetzen, kann auch nix werfen // Zeichenweises Kopieren for(int j=0; j<i; j++) kontoinhaber[j]=name[j]; // kopierschleife kann auch nix werfen }
-
Also ich find das Arbeiten über this viel sinnvoller. Sich so unpraktische Kürzel wie m_x zu entwickeln ist nicht wirklich praxisnah.
Auch ist es viel leichter Fremdcode später zu lesen, da es nicht durch eine Konvention festgelegt ist, sondern tatsächlich korrekte Syntax ist, die keine andere Variante zulässt.
Und den Parameter anders zu nennen als die Variable, verschlechtert meiner Ansicht nach auch die Lesbarkeit und fördert damit nicht dem Verständnis des Codes. Das einzige Argument, welches gegen eine Verwendung von this->x anstatt m_x spricht, ist, dass man - insbesodnere als Anfänger - vergisst, this-> zu schreiben und so seine Operationen auf der falschen Variable ausführt. Aber wenn man schon etwas länger programmiert, passiert einem sowas eigentlich nicht.
Ich bin übrigens auch kein Fan von getX und setX(Y) sondern benutze lieber X() und X(y), durch das überladen ist das ja kein Problem und es bleibt genauso eindeutig wie mit dem set/get Präfix.
-
void Konto::set_kontoinhaber(const char* name) { std::size_t len = 0; // std::size_t ist positive Ganzzahl, da Größe. Fall nicht erlaubt: unsigned int while(name[len++]); delete [] m_kontoinhaber; m_kontoinhaber = new char[i]; for (std::size_t pos = 0; pos < len; ++pos) m_kontoinhaber[pos] = name[pos]; }... so ist doch netter ...
-
@(D)Evil: und wieder nicht exception safe. Und es muss "new char[len];" heissen, "i" ist nicht definiert.
@all: ist euch allen das (exception safety) eigentlich *so* egal, oder seht/glaubt/versteht ihr es nur nicht?
@noNeed 4 aNick: es IST praxisnah, wie man an hunderten und tausenden von (kommerziellen, hobby-, ...) Source-Codes sieht die m_x verwenden. Dass du es nicht magst ist deine Sache, bloss mach dich bitte nicht lächerlich indem du behauptest es wäre nicht praxisnah.
-
hustbaer schrieb:
Davon abgesehen finde ich es grausig this-> zu schreiben. "this" gibts für Fälle wo man den "this" Zeiger irgendwo übergeben muss, also für Dinge wie "ptr->Foo(this)". IMO sicher NICHT dafür dass man zwischen Membern und nicht Membern mit gleichem Namen unterscheiden kann. BTW: wenn man "m_" für Member verwendet hat man das Problem auch gleich garnicht.
In meinem Oberstübchen rumpelt was bezüglich this. Ich meine mich erinnern zu können, dass es bei Verwendung von templates angeraten sein kann, this->blabla zu benutzen. Hatte meine ich was mit Vererbung und name-lookup zu tun, ich suchs mal raus...
/edit:
Aalso. Bei abhängigen Basisklassen á latemplate <typename T> class D : public B<T> { };ist this-> nötig, um
a) virtuelle Funktionen der abhängigen Basisklasse aufzurufen (einB<T>::foo()würde virtualität verhindern)
b) Den lookup zu verzögern, wenn es z.B. eine Spezialisierung der Basisklasse vorhanden ist.
Ist im aktuellen Problem zwar nicht anwendbar, aber ganz ohne this-> kommt man nicht aus
-
Yo ich weiss.
Templates, besonders mit dependent base classes, sind eine ganz eigene Sache.
Ich denke es war klar was ich meinte, nämlich die verwendung von "this->" in "normalem" Code -- der eben ohne das auskommt, solange man nicht gerade Parameter/lokale Variablen gleich nennt wie Member.
Was IMO komplett pöse weil verwirrend ist.
-
Hi,
ich empfinde die Verwendung von "this->" (mal abgesehen von der meistens unnötigen Tipparbeit) eine Einschränkung der Generizität ... ebenso wie die direkte Qualifizierung mittels std:: - und lasse Beides deswegen (und die "m_"-Notation ebenfalls).

Gruß,
Simon2.
-
Simon2 schrieb:
ich empfinde die Verwendung von "this->" (mal abgesehen von der meistens unnötigen Tipparbeit) eine Einschränkung der Generizität ... ebenso wie die direkte Qualifizierung mittels std:: - und lasse Beides deswegen (und die "m_"-Notation ebenfalls).

Was hat den this und std:: mit Generizität zu tun?
-
Simon2 schrieb:
Hi,
ich empfinde die Verwendung von "this->" ... eine Einschränkung der Generizität ...
Es ist genau umgekehrt, da manche Konstrukte eben nur dann funktionieren, wenn man "this->" benutzt. Ergo man muß nur etwas mehr tippen verliert aber nichts.
Simon2 schrieb:
ebenso wie die direkte Qualifizierung mittels std::
Der unqualifizierte Zugriff auf den Namesraum std via using Direktive führt doch Namensräume ad absurdum. Gerade C++ Projekte sind meist keine Ex und Hopp Projekte, so daß es sehr sinnvoll ist die Lesbarkeit des Programmcodes sicherzustellen. std::vector ist nun einmal sehr viel informativer als vector. Letzteres kann irgend ein vector aus irgend einem Namensraum sein.
-
Runde 126 schrieb:
Simon2 schrieb:
ich empfinde die Verwendung von "this->" (mal abgesehen von der meistens unnötigen Tipparbeit) eine Einschränkung der Generizität ... ebenso wie die direkte Qualifizierung mittels std:: - und lasse Beides deswegen (und die "m_"-Notation ebenfalls).

Was hat den this und std:: mit Generizität zu tun?
Ma legt über den Namen hinaus bereits einen "Gültigkeitsbereich" (meint nicht C++-Scope - finde gerade keine bessere Bezeichnung) fest.
Was, wenn in einer späteren Version nicht mehr std::cout, sondern das semantisch identische myOwn::cout verwendet werden soll ?
Was, wenn eine Membervariable später "ausgelagert" wird (z.B. in den globalen Namensraum) ?=> Jeweils an 1000 Codestellen rumändern, statt da, wo es hingehört: In der Deklaration:
// Version 1: // A.h struct A { int x, y; A(); void f() const; void g(); }; // A.cpp #include <iostream> using std::cout; A::A() : x(0), y(0) {} void A::f() const { cout << x; } void A::g() { f(); ++y; }Zwischenzeitlich haben sich die Anforderungen ein wenig geändert, so dass f() und x eigentlich nicht mehr viel mit A zu tun haben.
// Version 2: // A.h struct A { int y; void g(); }; // A.cpp #include <MyTools> using myTools::cout; static int x = 0; void f() { cout << x; } // Code identisch mit oben void A::g() { f(); ++y; } // Code identisch... später alles ausgelagert:
// Version 2: // A.h struct A { int y; void g(); }; // A.cpp #include <MyTools> using myTools::f; void A::g() { f(); ++y; } // Code identischDas wäre mit this->x und this->f() mehr Getippe (mit mehr Fehlerpotential) geworden.
Gruß,
Simon2.
-
~john schrieb:
Simon2 schrieb:
Hi,
ich empfinde die Verwendung von "this->" ... eine Einschränkung der Generizität ...
Es ist genau umgekehrt, da manche Konstrukte eben nur dann funktionieren, wenn man "this->" benutzt....
Du beziehst Dich hier auf einen Spezialfall im template-name-lookup. In allen anderen Fällen verbaut man sich aber gerade die Anbindung an templates durch die Forderung von "this->"....
D.h. ich habe die Wahl, ob von 100 Idiomen 99 oder 1 funktionieren. Welcher Ansatz generischer ist, kann man IMHO deutlich ablesen.~john schrieb:
...
Der unqualifizierte Zugriff auf den Namesraum std via using Direktive führt doch Namensräume ad absurdum...Wieso sollte das der Fall sein ?
Im Gegenteil: Dass ich an einer zentralen Stelle "umswitchen" kann, stärkt die Bedeutung von namespaces.~john schrieb:
...Letzteres kann irgend ein vector aus irgend einem Namensraum sein.
Eben: Das nennt man Generische Programmierung !
Muss man nicht mögen oder einsetzen, ich halte es aber (gerade in größeren Projekten) für einen großen Vorteil.
Mit demselben Argument müsste man sonst auch overloading bei Funktionen wieder abschaffen, weil "... man ja gar nicht mehr sieht, welche Funktion aufgerufen wird...".Gruß,
Simon2.
-
Simon2 schrieb:
Was, wenn in einer späteren Version nicht mehr std::cout, sondern das semantisch identische myOwn::cout verwendet werden soll ?
Das wäre im Extremfall eine automatische Suchen&Ersetzen Aktion - kein Drama, da ja der Name "std::cout" eindeutig ist. Was bei "cout" nicht gewährleistet ist. Wenn man den Ausgabestrom verändern können will, so sollte man das auch im Design berücksichtigen. Referenzen von std::cout oder anderen std::ostream lassen sich leicht erzeugen. Man sollte im Design immer darauf vorbereitet sein, daß man den Ausgabestrom umlenken kann,w enn man einen fest kodierten Bezug auf eine globale Variable nimmt (std::cout), ist das eindeutig nicht der Fall, und dann muß man mit den Defiziten leben.