" // Neue Tabellenzeile <<""<"<"<"<"<"<<endl <<"KontoNr" <<" | Name" <<" | Datum" <<" | Bew-Art" <<" | Betrag" <<endl; CKontobewegung K; while(in.read((char*)&K,sizeof(K))) print_KB(o,K); if (!in.eof()) ShowMessage("Fehler bei read"); o<<""<<endl; } void KBToHTML(char* ifn, char* htmlfn) { ifstream in(ifn,ios::binary); ofstream o(htmlfn); o<<""<<endl <<""<<endl <<"<TITLE>" <"<<endl <<""<<endl <<""<<endl; PrintTable(in, o); o<<""<<endl; o<<""<<endl; o.close(); // überflüssig } void __fastcall TForm1::KBToHtmlClick(TObject *Sender) { Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
205
206
12 Lösungen
OpenDialog1->InitialDir="c:\\test"; OpenDialog1->FileName="kb.dat"; OpenDialog1->Filter = "Daten (*.dat)|*.DAT"; if (OpenDialog1->Execute()) { KBToHTML(OpenDialog1->FileName.c_str(),"c:\\test\\kb.html"); WideString url="file:///C|\\test\\kb.html"; CppWebBrowser1->Navigate(url,0,0,0,0); Form1->Caption="Kontobewegungen"; } } void __fastcall TForm1::FormCreate(TObject *Sender) { // Erzeuge das Verzeichnis, falls es nicht existiert AnsiString dir="c:\\test"; if (CreateDirectory(dir.c_str(),0)) ShowMessage("directory '"+dir+"' created"); }
12.3.20 Aufgabe 4.6.6
// Datei: d:\Loesungen\kap-4\4.6\KonsAppU.cpp
#include #include #include #include
<stdio.h> <stdlib.h> <string.h>
USERES("ConsApp.res"); #include using namespace std; //------ Aufgabe 1 --------------------------------------------------int Quersumme(int n) { // genau wie die Lösung 3.4.7.1 if (n<0) n=-n; // falls n < 0 int s=0; while (n>0) { s = s+n%10; n = n/10; } return s; } void Zeige_QuerSummen(int a, int b) { cout<<"Quersummen"<<endl; for (int i=a; i<=b; i++) cout<
12.3 Lösungen Kapitel 4
fib = f0 + f1; f1 = f0; f0 = fib; } return fib; } void zeige_Fibonacci(int n) { cout<<"Fibonacci-Zahlen:"<<endl; for (int i=0; i
207
208
} void zeige_3nplus1(int n) { for (int i=0; i>Menuewahl; int n; if (Menuewahl=='1') Zeige_QuerSummen(1,20); if (Menuewahl=='2') zeige_Fibonacci(50); if (Menuewahl=='3') { cin>>n; zeige_Primzahlen(n); } if (Menuewahl=='4') { cin>>n; zeige_PythagZahlentripel(n); } if (Menuewahl=='5') { cin>>n; zeige_3nplus1(n); } } return 0; }
12.3.21 Aufgabe 4.6.7
// Datei: d:\Loesungen\kap-4\4.6\StrStrU.cpp
#include "StrStrU.h" //----- Aufgabe 4.6.7 -----------------------------------------------#include <string> #include <sstream> using namespace std; double StrToFloat(string s) { Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
209
istringstream is(s); is.exceptions(ios::failbit); double d; is>>d; return d; } int StrToInt(string s) { istringstream is(s); is.exceptions(ios::failbit); int i; is>>i; return i; } void TestStrToFloat(string s) { ostringstream out; out<<"'"<<s<<"': "<<StrToFloat(s); Form1->Memo1->Lines->Add(out.str().c_str()); } void TestStrToInt(string s) { ostringstream out; out<<"'"<<s<<"': "<<StrToInt(s); Form1->Memo1->Lines->Add(out.str().c_str()); } void TestStrTo() { TestStrToInt("123"); TestStrToInt("123 "); TestStrToInt("12 3"); TestStrToInt("12A3"); TestStrToInt(" 123A"); TestStrToFloat("1.23"); TestStrToFloat("123 "); TestStrToFloat("1.2 3"); TestStrToFloat("1.2A3"); TestStrToFloat(" 1.23A"); }
// // // // // // // // // //
123 123 12 12 123 1.23 123 1.2 1.2 1.23
void __fastcall TForm1::Button1Click(TObject *Sender) { TestStrTo(); }
12.3.22 Aufgabe 4.7
// Datei: d:\Loesungen\kap-4\4.7\AssContU.cpp
#include "AssContU.h" #include "\Loesungen\CppUtils\TimeUtils.cpp" #include "\Loesungen\CppUtils\StringUt.cpp" /* In StringUt.cpp ist auch die folgende Funktion:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
210
12 Lösungen
string getline_BUG_workaround(string z) { // BCB5 Bug: When using the global function getline(istream, std::string) // to fill a string, it adds an extra null terminator to the string. This // nullterminator is separate from the null terminator for the underlying // char*. // Dieser Bug ist im Service-Pack zum C++Builder5 behoben. int n=z.length(); char x=z[n-1]; if (x=='\0') z.erase(n-1,1); // nur zum Testen: n=z.length(); x=z[n-1]; return z; } */ //-------- Aufgabe 1 ---------------------------------------------#include <map> using namespace std; typedef typedef typedef InfoSys
int ValueType; int KeyType; map InfoSys; m;
bool ValueToKey(KeyType Key,ValueType& Value) { InfoSys::iterator pos=m.find(Key); bool found=(pos!=m.end()); if (found) Value=pos->second; return found; } void __fastcall TForm1::InfoSystClick(TObject *Sender) { CHRTimer t; ValueType data; t.Start(); bool found=ValueToKey(StrToInt(Edit1->Text), data); t.End(); if (found) Memo1->Lines->Add(data); else Memo1->Lines->Add("nicht gefunden!"); Memo1->Lines->Add(t.TimeStr()); /* Aufgrund des logarithmischen Komplexität von m.find ist wegen log(1000) ~ log(2^10) = 10 log(1000000) = log(1000*1000) ~ log(2^20) = 20 eine Verdoppelung der Suchzeiten zu erwarten. Tatsächlich konnten aber keine Unterschiede beobachtet werden: Gemessene Suchzeiten bei 1000 Elementen: 7,71048793979115E-5 Sekunden 7,54286863675221E-5 7,79429759131062E-5 7,87810724283008E-5 Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
7,79429759131062E-5 Gemessene Suchzeiten bei 1000000 Elementen: 9,7219195762584E-5 7,54286863675221E-5 8,12953619738849E-5 7,79429759131062E-5 7,12382037915486E-5 */ } void __fastcall TForm1::CreateDataClick(TObject *Sender) { m.clear(); const int max=100000; CHRTimer t; t.Start(); for (int i=0;i<max; i++) m[i]=i; t.End(); Memo1->Lines->Add(t.TimeStr()); /* Gemessene Zeiten: 100.000 map-Elemente: 1,5 Sekunden 1.000.000 map-Elemente: 17 Sekunden */ } //-------- Aufgabe 2 ---------------------------------------------#include <set> using namespace std; set RndSet; int NewRand(unsigned int Max) { if (RndSet.size()<Max) { unsigned int Anzahl_vorher=RndSet.size(), i; while (RndSet.size()==Anzahl_vorher) // Endlosschleife, falls { // s.size()>=Max i=rand()%Max; RndSet.insert(i); } return i; } else return -1; // oder Exception auslösen } // Diese Lösungsalternative ist mit jedem Container möglich, der eine // Funktion wie find hat: int NewRand1(unsigned int Max) { if (RndSet.size()<Max) { unsigned int i; do // Endlosschleife, falls RndSet.size()>=Max i=rand()%Max; while (RndSet.find(i)!=RndSet.end()); RndSet.insert(i); return i; } else return -1; // oder Exception auslösen } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
211
212
void __fastcall TForm1::NewRandClick(TObject *Sender) { int n=10; for (int i=0; iLines->Add(::NewRand1(n)); } //-------- Aufgabe 3 ---------------------------------------------#include <set> #include set <string> Woerterbuch; typedef vector<string> Container; void Woerterbuch_lesen(char* ifn) { Container c; ifstream in(ifn); in.exceptions(ios::badbit); if (in) { string z; while (getline(in,z)) { z=getline_BUG_workaround(z); parseString(z,c); for (Container::iterator i=c.begin();i!=c.end(); i++) Woerterbuch.insert(*i); c.clear(); } } in.close(); // überflüssig, aber kein Fehler } void Text_pruefen(char* ifn) { Container c; ifstream in(ifn); in.exceptions(ios::badbit); if (in) { string z; while (getline(in,z)) { z=getline_BUG_workaround(z); parseString(z,c); for (Container::iterator i=c.begin();i!=c.end(); i++) { if (Woerterbuch.find(*i)==Woerterbuch.end()) { // nicht im Wörterbuch enthaltenene Wörter ausgeben: Form1->Memo1->Lines->Add(AnsiString(z.c_str())+ ": "+AnsiString(i->c_str())); } } c.clear(); } } in.close(); // überflüssig, aber kein Fehler } void RechtschrPruefung() { Woerterbuch_lesen("faust.txt"); cout<<Woerterbuch.size()<<endl; Text_pruefen("faust1.txt"); // In Faust1.txt wurden gegenüber Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
213
// faust.txt einige Worte verändert } void __fastcall TForm1::RechtSchrPrClick(TObject *Sender) { RechtschrPruefung(); } //-------- Aufgabe 4 ---------------------------------------------typedef multimap<string,int> MMType; MMType mm; void MakeXRef(vector<string> v) // Ein XRef-Generator in 13 Zeilen { // erzeugt eine Crossreference-Liste aus den Strings eines Vektors Container c; int line=1; for (vector<string>::iterator i=v.begin();i!=v.end(); i++) { parseString(*i,c); for (Container::iterator token=c.begin();token!=c.end(); token++) mm.insert(make_pair(*token,line)); c.clear(); line++; } } void MakeXRef(char* fn) // fn: Dateiname { // erzeugt eine Crossreference-Liste aus den Wörtern und ihren Zeilennummern Container c; ifstream in(fn); if (in) { string z; int line=1; while (getline(in,z)) { z=getline_BUG_workaround(z); parseString(z,c); for (Container::iterator token=c.begin();token!=c.end(); token++) mm.insert(make_pair(*token,line)); c.clear(); line++; } } } #include <sstream> void PrintXRef(char* fn=0) { // Schreibt eine XRef-Liste in ein Memo oder in die Datei mit dem Namen fn ofstream of; if (fn!=0) of.open(fn); for (MMType::iterator i=mm.begin(); i!=mm.end(); ) { ostringstream out; MMType::iterator k, first=mm.lower_bound(i->first); MMType::iterator j, last=mm.upper_bound(i->first); k=first; out<first<<": "; for (j=first; j!=last; j++) { out << " "<<j->second; i++; // Mit jedem gefundenen Wert hochzõhlen }; if (fn!=0) of<Memo1->Lines->Add(out.str().c_str()); } } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
214
void __fastcall TForm1::XRefClick(TObject *Sender) { vector<string> v; v.push_back("Alle meine Entchen"); v.push_back("schwimmen auf dem See,"); v.push_back("schwimmen auf dem See,"); MakeXRef(v); PrintXRef(); mm.clear(); MakeXRef("AssContU.cpp"); PrintXRef(); MakeXRef("faust.txt"); PrintXRef("faust.xref"); } //-------- Aufgabe 5 ---------------------------------------------#include "\Loesungen\CppUtils\KBDecl.cpp" fstream f; using namespace std; typedef multimap TKBMap; TKBMap km; void MMReadKeys(char* fn) { CKontobewegung K; f.open(fn,ios::in|ios::out|ios::binary); int pos=f.tellg(); while (f.read((char*)&K,sizeof(K))) { km.insert(make_pair(K.KontoNr,pos)); pos=f.tellg(); } f.clear(); // Damit spätere seek-Zugriffe funktionieren } void __fastcall TForm1::KeysLesenClick(TObject *Sender) { OpenDialog1->InitialDir="c:\\test"; OpenDialog1->FileName="kb.dat"; OpenDialog1->Filter = "KB-Dateien (*.dat)|*.dat"; if (OpenDialog1->Execute()) { MMReadKeys(OpenDialog1->FileName.c_str()); KeySuchen->Enabled=true; Sortieren->Enabled=true; } } bool MMFind(int Key, CKontobewegung& K) { TKBMap::iterator i=km.find(Key); if (i!=km.end()) { f.seekg(i->second); f.read((char*)&K,sizeof(K)); return true; } else return false; } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
void __fastcall TForm1::KeySuchenClick(TObject *Sender) { int Key=StrToInt(Edit1->Text); CKontobewegung K; if (MMFind(Key, K)) // hier nur eine ganz "primitive" Ausgabe: Memo1->Lines->Add(IntToStr(K.KontoNr)+": "+K.NameInhaber); else Memo1->Lines->Add(Edit1->Text+": nicht gefunden."); } void MMSort(char* outfn) { CKontobewegung K; ofstream out(outfn,ios::binary); TKBMap::iterator i; for (i=km.begin(); i!=km.end(); i++) { f.seekg(i->first); f.read((char*)&K,sizeof(K)); out.write((char*)&K,sizeof(K)); } out.close(); } void __fastcall TForm1::SortierenClick(TObject *Sender) { OpenDialog1->InitialDir="c:\\test"; OpenDialog1->FileName="kb.sor"; OpenDialog1->Filter = "sortierte KB-Dateien (*.sor)|*.sor"; if (OpenDialog1->Execute()) { MMSort(OpenDialog1->FileName.c_str()); } } //-------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { KeySuchen->Enabled=false; Sortieren->Enabled=false; // Erzeuge das Verzeichnis, falls es nicht existiert AnsiString dir="c:\\test"; if (CreateDirectory(dir.c_str(),0)) ShowMessage("directory '"+dir+"' created"); }
12.3.23 Aufgabe 4.8
// Datei: d:\Loesungen\kap-4\4.8\NumKlassU.cpp
#include "NumKlassU.h" #include "\Loesungen\CppUtils\TimeUtils.cpp" // Nur ein "\" bei Pfadangaben!!! //------- Aufgaben 4.8.1 --------------------------------------------//------- Aufgabe 1 --------------------------------------------------
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
215
216
#include using namespace std; AnsiString ComplexToStr(complex c) { return FloatToStr(real(c))+"+"+FloatToStr(imag(c))+"i"; } void __fastcall TForm1::QuadGlClick(TObject *Sender) { complex<double> a(2,3), b(4,-5), c(-7,8), x1, x2; // complex<double> a(2,0), b(4,0), c(-7,0), x1, x2; x1= (-b+sqrt(b*b - 4.0*a*c))/(2.0*a); x2= (-b-sqrt(b*b - 4.0*a*c))/(2.0*a); Form1->Memo1->Lines->Add(ComplexToStr(x1)); Form1->Memo1->Lines->Add(ComplexToStr(x2)); // Probe: Form1->Memo1->Lines->Add(ComplexToStr(a*x1*x1+b*x1+c)); Form1->Memo1->Lines->Add(ComplexToStr(a*x2*x2+b*x2+c)); } // Mit typedef lassen sich die Datentypen etwas einfacher schreiben: typedef complex<double> dcomplex; AnsiString DComplexToStr(dcomplex c) { return FloatToStr(real(c))+"+"+FloatToStr(imag(c))+"i"; } //------- Aufgabe 2 -------------------------------------------------void __fastcall TForm1::EinheitswurzelnClick(TObject *Sender) { const int n=100; complex<double> w(cos(2*M_PI/n),sin(2*M_PI/n)); for (int i=0; i z=pow(w,i); Memo1->Lines->Add(ComplexToStr(pow(z,n))); } } //------- Aufgabe 3 -------------------------------------------------#include "\Loesungen\cpputils\GraphUtils.cpp" int Funktion=0; void __fastcall TForm1::ScrollBar1Change(TObject *Sender) { Funktion=1; if (Funktion >5) Funktion=1; double x0, y0, x1, y1, dx, dy; if (Funktion==1) { x0=-2; y0=-0.2; x1=2; y1=5; dx=0.5; dy=0.5;} else if (Funktion==2) { x0=-1; y0=0; x1=6; y1=100; dx=1; dy=10;} else if (Funktion==3) { x0=-2; y0=-2; x1=2; y1=4; dx=1; dy=1;} else if (Funktion==4) { x0=-2; y0=-6; x1=8; y1=10; dx=1; dy=1;} Image1->Canvas->Brush->Color=clWhite; // lösche das alte Bild Image1->Canvas->Rectangle(0,0,Image1->ClientWidth, Image1->ClientHeight); Gitternetz(Form1->Image1,x0,x1,dx, Image1->ClientWidth, y0,y1,dy, Image1->ClientHeight); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
Image1->Canvas->Pen->Color = clRed; for (int px=0; px<=Image1->Width-/*–*/1; px++) { // transformiere px in Welt-Koordinaten double x=x_Welt(px, x0, x1, Form1->Image1->Width); double y; dcomplex z(x,ScrollBar1->Position/10.0); // if (Funktion==1) y= abs(sin(z)); else if (Funktion==2) y= abs(exp(z)); else if (Funktion==3) y= abs(z*z); else if (Funktion==4) y= abs(z*sin(z)); // transformiere y in Bildkoordinaten int py=y_Bildschirm(y, y0, y1, Image1->Height); if (px == 0) Image1->Canvas->MoveTo(px,py); else Image1->Canvas->LineTo(px,py); } } //-------
Aufgaben 4.8.2 --------------------------------------------
void Kreuz(int x, int y, int w) { Form1->Image1->Canvas->MoveTo(x-w,y); Form1->Image1->Canvas->LineTo(x+w,y); Form1->Image1->Canvas->MoveTo(x,y+w); Form1->Image1->Canvas->LineTo(x,y-w); } // Die folgenden Variablen nur deshalb global, da // die Teilaufgaben in eigenen Funktionen behandelt // werden: double sxy, sx, sy, sxx, ym, xm; const int n=100; // n=2; n=100; n=10000; #include using namespace std; valarray<double> x(n), y(n); void Kleinste_Quadrate_mit_valarrays() { // Aufgabe a) sxy = (x*y).sum(); sx = x.sum(); sy = y.sum(); sxx = (x*x).sum(); xm = sx/n; ym = sy/n; } void Kleinste_Quadrate_mit_Arrays() { // Aufgabe b) sxy=0; for (int i=0; i
217
218
12 Lösungen
// Aufgabe 4.8.2.1 const double a_=2, b_=3; // Aufgabe 4.8.2.2 a): rnd=0; // Aufgabe 4.8.2.2 b): rnd>0; double rnd=5; // mit positivem Wert ergibt sich eine Streuung for (int i=0; iLines->Add("Kleinste Quadrate: "); Memo1->Lines->Add("a_="+FloatToStr(a_)+ " a="+FloatToStr(a)); Memo1->Lines->Add("b_="+FloatToStr(b_)+ " b="+FloatToStr(b)); // Aufgabe 4.8.2.3 // zeichen die Punkte als Rechtecke // Weltkoordinaten int x0=x.min(); int x1=x.max(); int y0=y.min(); int y1=y.max(); Image1->Canvas->Pen->Color = clRed; // Kreuze rot zeichnen for (int i=0; iWidth); int py=y_Bildschirm(y[i], y0, y1, Image1->Height); Kreuz(px, py, 5); } Image1->Canvas->Pen->Color = clBlack; // Gerade schwarz // Anfangspunkt der Geraden int px=x_Bildschirm(0, x0,x1,Image1->Width); int py=y_Bildschirm(a*0+b, y0,y1,Image1->Height); Image1->Canvas->MoveTo(px,py); // Endpunkt der Geraden px=x_Bildschirm(n-1, x0,x1,Image1->Width); py=y_Bildschirm(a*(n-1)+b, y0,y1,Image1->Height); Image1->Canvas->LineTo(px,py); }
12.4 Lösungen Kapitel 5
12.4.1 Aufgaben 5.2.19
// Datei: d:\Loesungen\kap-5\5.2\ExprU.cpp
#include "ExprU.h"
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
void __fastcall TForm1::FormCreate(TObject *Sender) { // Erzeuge das Verzeichnis, falls es nicht existiert AnsiString dir="c:\\test"; if (CreateDirectory(dir.c_str(),0)) ShowMessage("directory '"+dir+"' created"); } //----- Aufgabe 1. -----------------------------------------------#include using namespace std; int i=0; void Aufgabe1() { int i=2,j=i,k=-6, m=8, n=10000, x=0, h[5]; unsigned u=40000u,v=u; // Aufgaben int a= k/i/j+n/j; // a = ((k/i)/j)) + (n/j); // = -3/2 + 5000 = 4999 Form1->Memo1->Lines->Add(a); Form1->Memo1->Lines->Add(typeid(k/i/j+n/j).name()); // int int b= k/i/j++ + m/j++; // Das Ergebnis ist undefiniert, da es davon abhängt, ob j++ zuerst im // ersten oder im zweiten Teilausdruck erhöht wird Form1->Memo1->Lines->Add(b); Form1->Memo1->Lines->Add(typeid(k/i/j++ + m/j++).name()); // int int c= i*4%-k*3; // c = ((i*4)%(-k))*3; // = (8%6)*3=6 // Alle Operatoren sind multiplikative Operatoren und werden linksassoziativ ausgewertet: Form1->Memo1->Lines->Add(c); Form1->Memo1->Lines->Add(typeid(i*4%-k*3).name()); // int int d= k/-m-2; // d = (k/-m)-2; // = 0-2 = -2 Form1->Memo1->Lines->Add(d); Form1->Memo1->Lines->Add(typeid(k/-m-2).name()); // int double e= m*n/u/v; // Obwohl die linke Seite den Datentyp double hat, wird der Ausdruck als // Ganzzahlwert ausgewertet. // e = ((m*n)/u)/v; // = 2/40000 = 0 Form1->Memo1->Lines->Add(e); Form1->Memo1->Lines->Add(typeid(m*n/u/v).name()); // unsigned int int f= ++::i%++i; // f = ++::i%++i; // = 1%3 = 1 Form1->Memo1->Lines->Add(f); Form1->Memo1->Lines->Add(typeid(++::i%++i).name()); // int int g = sizeof 17-2; // g = (sizeof 17)-2; // = 4-2 = 2 Form1->Memo1->Lines->Add(g); Form1->Memo1->Lines->Add(typeid(sizeof 17-2).name()); // unsigned int while(x<=5) h[x]=x++; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
219
220
12 Lösungen
// Das Ergebnis der Anweisungen in der Schleife ist // nicht definiert, da nicht klar ist, ob a[x] vor oder nach der Erhöhung // von x angesprochen wird. // Außerdem wird das Arrayelement A[5] angesprochen, für das // kein Speicherplatz reserviert ist. Form1->Memo1->Lines->Add(typeid(x++).name()); // int } void __fastcall TForm1::Aufg1Click(TObject *Sender) { Aufgabe1(); } //----- Aufgabe 2. -----------------------------------------------void __fastcall TForm1::Aufg2Click(TObject *Sender) { // a) int w=1; for (int i=0; i<8*sizeof(w); i++) { Memo1->Lines->Add(IntToStr(i)+": "+IntToStr(w)); // mit "+" werden die verschiedenen Strings zu // einem einzigen "zusammengeklebt" w = w<<1; } // b) w=1; for (int i=0; i<8*sizeof(w); i++) Memo1->Lines->Add(IntToStr(i)+": "+IntToStr(w<
b) (a<=b & c<=d);// ... Da der Operator <= eine höhere Priorität hat als &, wird dieser Ausdruck wie (a<=b) & (c<=d) behandelt. Die beiden Operanden (a<=b und (c<=d) sind boolesche Werte, die für & in Ganzzahlwerte konvertiert werden. Dabei wird false zu 0 und true zu 1. Für die verschiedenen Kombinationen ergibt sich so dasselbe Ergebnis wie unter a) p true true false false
q true false true false
int(p) 1 1 0 0
int(q) 1 0 1 0
p&q 1 0 0 0
p&&q true false false false
c) (a<=b == c<=d);// ... Da der Operator == eine höhere Priorität hat als <=, wird dieser Ausdruck wie a<=(b == c)<=d behandelt. Der Ausdruck (a<=b == c<=d) wird auf den ersten Blick oft als (a<=b) == (c<=d) interpretiert.
for (int a=0; a<10; a++) for (int b=0; b<10; b++) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
221
for (int c=0; c<10; c++) for (int d=0; d<10; d++) // if ( (a<=b == c<=d)==((a<=b) == (c<=d)) ) if ( ((a<=(b == c))<=d)!=((a<=b) == (c<=d)) ) Form1->Memo1->Lines->Add(IntToStr(a)+" "+IntToStr(b)+" "+IntToStr(c)+" " +IntToStr(d)); // d) for (int i=0; i<5; i++) if (i&1) Form1->Memo1->Lines->Add(IntToStr(i)); // // // //
Für ungerade Werte von i hat i&1 den Wert 1 und für gerade den Wert 0. Da der Wert 1 in true und 0 in false konvertiert wird, ist die Bedingung für ungerade Werte von i erfüllt.
// e) for (int i=0; i<5; i++) if (i&1==0) Form1->Memo1->Lines->Add(IntToStr(i)); // Da == eine höhere Priorität hat als &, wird dieser Ausdruck wie // i&(1==0) interpretiert und ist immer false. Viele Leser // interpretieren den Ausdruck auf den ersten wie (i&1)==0 } void __fastcall TForm1::Aufg3Click(TObject *Sender) { Aufgabe3(); } //----- Aufgabe 4. -----------------------------------------------#include <stdio.h> // notwendig für sprintf void Aufgabe4() { double x = 3.14; int a=1,b=2; // a) int s = a?b:x; // s=2 Form1->Memo1->Lines->Add(typeid(a?b:x).name()); // double // b) { int s = (x > 0) ? 1 : (x < 0) ? -1 : 0; s = (x > 0) ? 1 : ((x < 0) ? -1 : 0); if (x>0) s=1; else if (x<0) s=-1; else s=0; } // c) for (int n=0;n<4;n++) { char s[100]; sprintf(s,"%d file%c found",n,(n==1)?' ':'s'); Form1->Memo1->Lines->Add(s); } // d) // Übersetzen Sie die Anweisung nach d) ins Deutsche. for (int n=0;n<4;n++) { char s[100]; sprintf(s,"%d Datei%s gefunden",n,(n==1)?"":"en"); Form1->Memo1->Lines->Add(s); } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
222
12 Lösungen
} void __fastcall TForm1::Aufg4Click(TObject *Sender) { Aufgabe4(); } //----- Aufgabe 5. -----------------------------------------------void Aufgabe5() { int i,j,k; i=3;j=1;k=2; k=i+++j; // k=(i++)+j=4, i=4 Form1->Memo1->Lines->Add("i="+IntToStr(i)+" j="+IntToStr(j)+" i=3;j=1;k=2; k= ++i+j++; // k=(++i)+(j++)=5, i=4, j=2 Form1->Memo1->Lines->Add("i="+IntToStr(i)+" j="+IntToStr(j)+" i=3;j=1;k=2; k= -i+++j; // k=(-(i++))+j=-2, i=4, Form1->Memo1->Lines->Add("i="+IntToStr(i)+" j="+IntToStr(j)+" i=3;j=1;k=2; k= +i+-j; // k=(+i)+(-j)=2 Form1->Memo1->Lines->Add("i="+IntToStr(i)+" j="+IntToStr(j)+" i=3;j=1;k=2; k= i--+--j; // k=(i--)+(--j)=3, i=2, j=0 Form1->Memo1->Lines->Add("i="+IntToStr(i)+" j="+IntToStr(j)+" }
k="+IntToStr(k)); k="+IntToStr(k)); k="+IntToStr(k)); k="+IntToStr(k)); k="+IntToStr(k));
void __fastcall TForm1::Aufg5Click(TObject *Sender) { Aufgabe5(); } //----- Aufgabe 6. -----------------------------------------------// a) void SetROH(const char* FileName) { SetFileAttributes(FileName,FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN); } // b) bool isSystemFile(const char* FileName) { DWORD attrib=GetFileAttributes(FileName); if (attrib!= 0xFFFFFFFF) return attrib&FILE_ATTRIBUTE_SYSTEM; else return false; // z. B. falls die Datei nicht existiert } // c) bool SetFileAttributeToHidden(const char* FileName) { DWORD attrib=GetFileAttributes(FileName); if (attrib!= 0xFFFFFFFF) return SetFileAttributes(FileName,attrib|FILE_ATTRIBUTE_HIDDEN); else return false; } // d) bool RemoveFileAttributeHidden(const char* FileName) { DWORD attrib=GetFileAttributes(FileName); if (attrib!= 0xFFFFFFFF) return SetFileAttributes(FileName,attrib&(~FILE_ATTRIBUTE_HIDDEN)); else return false; } #include <string.h> void Aufgabe6() Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
223
{ // Vor dem Aufruf dieser Funktion müssen die folgenden Dateien zuerst // angelegt werden: LPCTSTR LPCTSTR LPCTSTR LPCTSTR
FileName1="c:\\test\\test1.dat"; FileName2="c:\\test\\test2.dat"; FileName3="c:\\test\\test3.dat"; FileName4="c:\\test\\test4.dat";
SetROH(FileName1); if (isSystemFile(FileName2)) Form1->Memo1->Lines->Add(AnsiString(FileName2)+" ist Systemdatei"); else Form1->Memo1->Lines->Add(AnsiString(FileName2)+" ist keine Systemdatei"); if (SetFileAttributeToHidden(FileName3)) Form1->Memo1->Lines->Add(AnsiString(FileName3)+" Attribut Hidden gesetzt"); else Form1->Memo1->Lines->Add(AnsiString(FileName3)+" Attribut Hidden nicht gesetzt"); if (RemoveFileAttributeHidden(FileName4)) Form1->Memo1->Lines->Add(AnsiString(FileName4)+" Attribut Hidden gelöscht"); else Form1->Memo1->Lines->Add(AnsiString(FileName4)+" Attribut Hidden nicht gelöscht"); } void __fastcall TForm1::Aufg6Click(TObject *Sender) { Aufgabe6(); }
12.4.2 Aufgaben 5.3.4
1. n ungerade, d. h. n/2 = (n–1)/2 x x0 p = p*x n = n/2 x = x*x
n n0
p p0 p0*x0
(n0-1)/2 x0*x0
p*xn = (p0*x0)*(x0*x0)(n0–1)/2 = p0*x0*x0no–1 = p0*x0n0 = uv 2. n gerade, d. h. n/2 = n/2: x x0
n n0
n = n/2 x = x*x
p p0
n0/2 x0*x0
p*xn = p0*(x0*x0)(n0/2) = p0*x0n0 = uv 3.
s s0 i = i + 1 s = s + i
i i0 i0 + 1
s0 + i0 + 1
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
224
12 Lösungen
Damit gilt anschließend s = s0 + (i0 + 1) = 1 + 2 + ... + i0 + (i0 + 1) = 1 + 2 + ... + (i–1) + i 4. s s0
i i0
i = i + 1
i0 + 1
s = s + i*i
s0 + (i0+1)*(i0+1)
Damit gilt anschließend
s = 1*1 + 2*2 + ... + i*i
5. s s0 s = s + i*i i = i + 1
i i0
s0 + i0*i0 i0 + 1
Damit gilt anschließend s = 1*1 + 2*2 + ... + i0*i0 + i0*i0 = 1*1 + 2*2 + ... + 2*(i–1)*(i–1) und nicht die gefragte Invarianz.
12.4.3 Aufgabe 5.5
// Datei: d:\Loesungen\kap-5\5.5\BlockU.cpp
#include "BlockU.h" void verschachtelt() { int i,j,k=7; AnsiString s,t; { char j; { float i=0,j=i; { AnsiString i,j,k; i = "ckt"; j = "ra"; k = "vert"; // i, j, k: AnsiString s = s+k+j+i+" "; // i, j, k, s: AnsiString } i = 1; j = 2; // i, j: int t = FloatToStr(i)+" + "+FloatToStr(j)+" = "+FloatToStr(k);//i,j,k: int { AnsiString t,j,k,i=s; { char t,j,k; t = 's'; j = 'i'; k = 't'; // t, j, k: char s = AnsiString(' ')+j+t+k+ ' '; // s: AnsiString, t, j, k: char } { char t,j,k; t = 'a'; j = 'D'; k = 's'; // t, j, k: char Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
225
s = AnsiString(j) + t + k+s; // s: AnsiString, t, j, k: char } t = "nz sch"; j = "ja ga"; k = "ön "; // t, j, k: AnsiString s = s+j+t+k+i; // s, t, j, k, i: AnsiString } } } Form1->Memo1->Lines->Add(t); Form1->Memo1->Lines->Add(s); }
// t: AnsiString // s: AnsiString
void __fastcall TForm1::VerschachteltClick(TObject *Sender) { verschachtelt(); /* Erzeugt die Ausgabe: 1 + 2 = 7 Das ist ja ganz schön vertrackt */ }
12.4.4 Aufgabe 5.6
// Datei: d:\Loesungen\kap-5\5.6\SpeicherklU.cpp
#include "SpeicherklU.h" int Fibonacci_(int n) { int fib=0,f0=0,f1=1; for (int i=0; iLines->Add(IntToStr(Fibonacci(i))); }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
226
12 Lösungen
12.4.5 Aufgabe 5.7
// Datei: d:\Loesungen\kap-5\5.7\ifu.cpp
#include "ifu.h" //----- Aufgabe 1 -----------------------------------------------void __fastcall TForm1::Aufg1Click(TObject *Sender) { float x=17.1,Punkte; int i=0,j; bool b; // a) if (x==17.1) Memo1->Lines->Add("Volltreffer"); // Prüfungen auf Gleichheit mit einer Gleitkommavariablen haben nicht // immer das erwartete Ergebnis. // if // //
b) (i>=1 && i<=10) Edit1->Text = "Volltreffer"; Syntaktisch und semantisch korrekt. Verknüpfte Bedingungen müssen nicht geklammert werden.
// c) // if b && (i=j*x) Edit1->Text = "Volltreffer"; // Syntaxfehler: Die Bedingung in einer if-Anweisung muss in einer Klammer // stehen. "=" ist ein Zuweisung und keine Prüfung auf Gleichheit. // d) if (Punkte >= 0) Edit1->Text="Extremes Pech"; else if (Punkte >= 20) Edit1->Text="Ziemliches Pech"; else if (Punkte >= 40) Edit1->Text="Ein wenig Glück gehabt"; // Syntaktisch korrekt, aber „<" mit „>" verwechselt (der zweite oder // dritte else-Zweig wird nie erreicht). } //----- Aufgabe 2 -----------------------------------------------AnsiString NoteToStr(int Note) { if (Note==1) return "sehr gut!!!"; else if (Note==2) return "gut"; else if (Note==3) return "na ja"; else if (Note==4) return "schwach"; else if ((Note==5) || (Note==6)) return "durchgefallen"; else return "Undefinierte Note "+IntToStr(Note); } void __fastcall TForm1::NoteClick(TObject *Sender) { Memo1->Lines->Add(NoteToStr(1)); Memo1->Lines->Add(NoteToStr(2)); Memo1->Lines->Add(NoteToStr(3)); Memo1->Lines->Add(NoteToStr(4)); Memo1->Lines->Add(NoteToStr(5)); Memo1->Lines->Add(NoteToStr(6)); Memo1->Lines->Add(NoteToStr(7)); } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
//----- Aufgabe 3 -----------------------------------------------double LA_Summe,LB_Summe,MA_Summe,MB_Summe,MC_Summe,Summe; void LagMat(char Lagergruppe, char Materialgruppe) { if (Lagergruppe == 'A') { if (Materialgruppe == 'A') { LA_Summe = LA_Summe + Summe; MA_Summe = MA_Summe + Summe; } else if (Materialgruppe == 'B') { LA_Summe = LA_Summe + Summe; MB_Summe = MB_Summe + Summe; } else if (Materialgruppe == 'C') { LA_Summe = LA_Summe + Summe; MC_Summe = MC_Summe + Summe; } else ShowMessage("Unzulässige Materialgruppe in Lager A"); } else if (Lagergruppe == 'B') { if (Materialgruppe == 'A') { LB_Summe = LB_Summe + Summe; MA_Summe = MA_Summe + Summe; } else if (Materialgruppe == 'B') { LB_Summe = LB_Summe + Summe; MB_Summe = MB_Summe + Summe; } else ShowMessage("Unzulässige Materialgruppe in Lager B"); } else ShowMessage("Unzulässige Lagergruppe in Lager B"); } void __fastcall TForm1::MatLagClick(TObject *Sender) { LagMat('A','B'); } //----- Aufgabe 4 -----------------------------------------------struct CDatum { int Tag, Monat, Jahr; }; bool vorher (CDatum d1, CDatum d2) { if (d1.Jahr!=d2.Jahr) return d1.Jahr
Falls die boolesche Variable vorher_oder_gleich genau dann den Wert true erhalten soll, wenn das erste Datum zeitlich vor dem zweiten liegt oder gleich dem zweiten ist, muss lediglich die letzte Zeile auf <= geändert werden zu:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
227
228
// // }
12 Lösungen
return d1.Tag < d2.Tag;
void testDatumsvergleich(CDatum d1, CDatum d2) { AnsiString s1=IntToStr(d1.Tag)+"."+IntToStr(d1.Monat)+"."+ IntToStr(d1.Jahr); AnsiString s2=IntToStr(d2.Tag)+"."+IntToStr(d2.Monat)+"."+ IntToStr(d2.Jahr); if (vorher(d1,d2)) Form1->Memo1->Lines->Add(s1+" liegt vor "+s2); else Form1->Memo1->Lines->Add(s1+" liegt nicht vor "+s2); } void __fastcall TForm1::DatumsverglClick(TObject *Sender) { CDatum d1={1,2,3}; CDatum d2={1,2,3}; testDatumsvergleich(d1, d2); } //----- Aufgabe 5 -----------------------------------------------// Hier sind die Datentypen double (Funktion dEst) und Currency // (Funktion CEst) angemessen. // typedef double Gleitkommatyp; typedef float Gleitkommatyp; // Mit diesem typedef kann der Gleitkommatyp für die Est-Berechnung leicht // geändert werden, um die float- und die double-Version zu vergleichen. // a) int dEst(Gleitkommatyp x) { // Einkommensteuertarif nach § 32a (1) Gleitkommatyp est; x = (int(x)/54)*54; // Nur zulässig für Werte von x im Wertebereich // von int, bis 2.147.483.647 DM if (x <= 12365) est = 0; // Die Bedingung (0 <= x) && (x <= 12365) würde ESt für negative Werte // von x (negative Einkommen sind mit Abschreibungen oder Verlusten // möglich) undefiniert lassen. else if (x <= 58643) { // Die Bedingung (x > 12095) braucht hier nicht mehr geprüft zu // werden, da sie sich bereits aus der Negation der letzten Bedingung // ergibt. Gleitkommatyp y = (x-12312)/10000; // letzter Absatz (1) est = (91.19*y+2590)*y; } else if (x <= 120041) { Gleitkommatyp z = (x-58590)/10000; // letzter Absatz (1) est = (151.96*z + 3434)*z +13938; } else est = 0.53*x - 22843; return est; // Absatz 3, letzter Satz: runde den Steuerbetrag auf volle // DM ab. Das funktioniert aber nur im Wertebereich von int }; Currency CEst(Currency x) { // Einkommensteuertarif nach § 32a (1) Currency est; x=(x/540000)*540000; // speziell für Currency, schneidet auch // Nachkommastellen ab if (x <= 12365) est = 0; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
// Die Bedingung (0 <= x) && (x <= 12365) würde ESt für negative Werte // von x (negative Einkommen sind mit Abschreibungen oder Verlusten // möglich) undefiniert lassen. else if (x <= 58643) { Currency y = (x-12312)/10000; // letzter Absatz (1) est = (91.19*y+2590)*y; } else if (x <= 120041) { Currency z = (x-58590)/10000;// letzter Absatz (1) est = (151.96*z + 3434)*z +13938; } else est = 0.53*x -22843; return 10000*(est/10000); // so wird für Currency der Steuerbetrag auf // volle DM abgerundet (Absatz 3, letzter Satz) }; void EstVergl(int x,int Soll) { Form1->Memo1->Lines->Add(IntToStr(x)+": "+IntToStr(dEst(x))+", " + CurrToStr(CEst(x))+ " Soll="+IntToStr(Soll)); } void test_Est() { EstVergl(10800,0); EstVergl(16200,1020); EstVergl(21600,2484); EstVergl(27000,4000); EstVergl(32400,5570); EstVergl(37800,7193); EstVergl(43200,8870); EstVergl(48600,10599); EstVergl(54000,12381); EstVergl(59400,14217); EstVergl(64800,16129); EstVergl(70200,18129); EstVergl(75600,20218); EstVergl(81000,22396); EstVergl(86400,24663); EstVergl(91800,27018); EstVergl(97200,29461); EstVergl(102600,31994); EstVergl(108000,34615); EstVergl(113400,37324); EstVergl(118800,40123); } void test_compEst(int von, int bis, int delta) { // Diese Funktion zeigt an, bei welchen Werten sich das Ergebnis der float// bzw. double-Version dEst und das der Currency-Version cEst // unterscheiden. Bei meinen Tests waren alle double-Ergebnisse, aber nicht // alle float-Ergebnisse, gleich den Currency-Ergebnissen. for (int x=von; x<=bis; x+=delta) if (dEst(x)!=CEst(x)) Form1->Memo1->Lines->Add(FloatToStr(x)+": "+ FloatToStr(dEst(x))+" != "+FloatToStr(CEst(x))); } void __fastcall TForm1::EStClick(TObject *Sender) { test_Est(); test_compEst(0,1000000,1); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
229
230
12 Lösungen
} // b) int Splitting(double x) { return 2*dEst(x/2); } //----- Aufgabe 5.7.2 -------------------------------------------//----- Aufgabe 1 -----------------------------------------------void LagMatSwitch(char Lagergruppe, char Materialgruppe) { switch (Lagergruppe) { case 'A': switch (Materialgruppe) { case 'A': LA_Summe = LA_Summe + Summe; MA_Summe = MA_Summe + Summe; break; case 'B': LA_Summe = LA_Summe + Summe; MB_Summe = MB_Summe + Summe; break; case 'C': LA_Summe = LA_Summe + Summe; MC_Summe = MC_Summe + Summe; break; default: ShowMessage("Unzulässige Materialgruppe in Lager A"); } case 'B': switch (Materialgruppe) { case 'A': LB_Summe = LB_Summe + Summe; MA_Summe = MA_Summe + Summe; break; case 'B': LB_Summe = LB_Summe + Summe; MB_Summe = MB_Summe + Summe; break; default: ShowMessage("Unzulässige Materialgruppe in Lager B"); } default: ShowMessage("Unzulässige Lagergruppe in Lager B"); } } void __fastcall TForm1::NoteSwClick(TObject *Sender) { LagMatSwitch('A','B'); } //----- Aufgabe 2 -----------------------------------------------void __fastcall TForm1::DatumsverglSwClick(TObject *Sender) { AnsiString s= "Da die Abfragen nicht durch eine Prüfung auf Gleichheit mit einer " "Konstanten gebildet werden, ist eine Lösung mit switch nicht möglich."; Memo1->Lines->Add(s); } //----- Aufgabe 3 -----------------------------------------------void __fastcall TForm1::SteuerClick(TObject *Sender) { Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
231
AnsiString s= "Die Auswahl der Berechnungsvorschrift zur Berechnung der Einkommensteuer " "kann nicht in einer switch-Anweisung erfolgen, da die Bedingungen nicht " "durch eine Prüfung auf Gleichheit gebildet werden und der Datentyp der " "zum Vergleich herangezogenen Ausdrücke ein Gleitkommadatentyp ist."; Memo1->Lines->Add(s); } 12.4.6 Aufgaben 5.7.3 1. Aus Platzgründen steht „t“ für „true“ und „f“ für „false“. a) p
q
r
q or r
t t t t f f f f
t t f f t t f f
t f t f t f t f
t t t f t t t f
p
q
r
q and r
t t t t f f f f
t t f f t t f f
t f t f t f t f
t f f f t f f f
p
q
r
p and q
t t t t f f f f
t t f f t t f f
t f t f t f t f
t t f f f f f f
p and (q or r) t t t f f f f f
p and q
p and r
t t f f f f f f
t f t f f f f f
(p and q) or (p and r) t t t f f f f f
b) p or (q and r) t t t t t f f f
p or q
p or r
(p or q) and (p or r) t t t t t f f f
t t t t t t f f
t t t t t f t f
q or r
p and (q or r)
t t t f t t t f
t t t f f f f f
c) (p and q) or r t t t f t f t f
"(p and q) or r" und "p and (q or r)" sind also nicht gleichwertig.
2.
a) (x >= 0) and (x <= 100) b) (x = 1) or (c <> 'j') c) (i < 1) and (i > 10). Da diese Bedingung für kein i erfüllt sein kann, wird S2 nie ausgeführt.
3. S1: (1 <= x) and (x <= 10) S2: (10 < x) and (x <= 15) S3: (15 > x) S4: (x < 1)
4. Im letzten else-Zweig gilt die Negation der beiden vorangehenden Bedingungen: not((x >= y) and (x >= z)) and not ((y >= x) and (y >= z))
Diese sind wegen de Morgan gleichwertig mit: ((x < y) or (x < z)) and ((y < x) or (y < z))
Zweimal "ausmultiplizieren" mit den Distributivgesetzen ergibt: Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
232
12 Lösungen [(x
Hier wurden die 4 mit or verknüpften Ausdrücke extra mit eckigen Klammern zusammengefasst. Der erste dieser eckig geklammerten Ausdrücke hat immer den Wert false, während alle 3 weiteren jeder für sich gerade bedeuten, dass z das Maximum der drei Werte x, y und z ist. 5. In den Aufgaben 5.3.4.1 und 5.3.4.2 wurden bereits die folgenden beiden Invarianten nachgewiesen: n gerade:
n
// p*x = u n = n/2; x = x*x; n v // p*x = u
n ungerade:
// p*x p = p*x; n = n/2; x = x*x; n v // p*x = u
n
= u
v
v
Das sind aber gerade die Anweisungsfolgen, die durch if (n&1) n = n/2; x = x*x;
p = p*x;
ausgeführt werden, wenn n gerade oder ungerade ist. Damit gilt die gefragte Invarianz n
v
// p*x = u if (n&1) p = p*x; n = n/2; x = x*x; n v // p*x = u
6. Begründen oder widerlegen Sie, dass die Funktion median immer den mittleren der drei als Parameter übergebenen Werte liefert: T median (T a, T b, T c) // T irgendein Datentyp { if (a < b) if (b < c) return b; // a=c }
12.4.7 Aufgabe 5.8
// Datei: d:\Loesungen\kap-5\5.8\SchleifU.cpp
#include "SchleifU.h" void __fastcall TForm1::FormCreate(TObject *Sender) { EKapital->Text="1000"; EZinssatzVerzinsung->Text="3"; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
ELaufzeit->Text="10"; EDarlehen->Text="100000"; EZinssatzHypo->Text="6"; ETilgung->Text="2"; } //----- Aufgabe 5.8.3.1 ---------------------------------------------void Zinsen(Currency Kapital, Currency Zinssatz, int Laufzeit) { int Jahre = 0; if (Jahre < Laufzeit) do { Kapital = Kapital*(1 + Zinssatz/100); Jahre++; Form1->RichEdit1->Lines->Add(IntToStr(Jahre)+". Jahr: "+FloatToStrF(Kapital,ffFixed,8,2)); } while (Jahre < Laufzeit); } void __fastcall TForm1::BerechneZinsClick(TObject *Sender) { Currency Kapital=StrToCurr(EKapital->Text); Currency Zinssatz=StrToCurr(EZinssatzVerzinsung->Text); int Laufzeit=StrToCurr(ELaufzeit->Text); Zinsen(Kapital, Zinssatz, Laufzeit); } //----- Aufgabe 5.8.3.2 ---------------------------------------------void Hypothek(Currency Betrag, Currency Zinssatz, Currency Tilgungsrate) { Currency Restschuld = Betrag; Currency Annuitaet = Restschuld*(Zinssatz+Tilgungsrate)/100; int Jahre = 0; AnsiString s="Hypothek: "+CurrToStr(Restschuld)+": Annuitaet: "+CurrToStr(Annuitaet); Form1->RichEdit1->Lines->Add(s); while (Restschuld > 0) { Currency Zinsen = Restschuld*Zinssatz/100; Restschuld = Restschuld - (Annuitaet - Zinsen); Jahre++; s="Ende "+IntToStr(Jahre)+".Jahr: Restschuld="+CurrToStr(Restschuld)+ " Zinsen="+CurrToStr(Zinsen)+" Tilgung="+CurrToStr(Annuitaet-Zinsen); Form1->RichEdit1->Lines->Add(s); } }; void __fastcall TForm1::BerechneHypoClick(TObject *Sender) { Currency Darlehen = StrToCurr(EDarlehen->Text); Currency Zinssatz = StrToCurr(EZinssatzHypo->Text); Currency Tilgungsrate = StrToCurr(ETilgung->Text); Hypothek(Darlehen, Zinssatz, Tilgungsrate); } //----- Aufgabe 5.8.3.3 ---------------------------------------------void __fastcall TForm1::for_intClick(TObject *Sender) { // typedef double T; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
233
234
// // // // //
12 Lösungen
typedef float T; for (T d=0;d<=1;d+=0.1) RichEdit1->Lines->Add(FloatToStr(d)); return; a) Diese for-Schleife gibt die 10 Werte 0, 0.1, 0.2, ..., 0.9, 1.0 aus:
// for (double d=0;d<=1;d+=0.1) for (int i=0;i<=10;i++) RichEdit1->Lines->Add(FloatToStr(i/10.0)); // b) Diese for-Schleife gibt die n Werte ug, ug+d, ug+2*d, ..., ug+(n-1)*d, ug+n*d=og aus: double ug=0; double og=1; double d=1.0/10; int n=(og-ug)/d+0.5; // Die Addition von 0.5 rundet den Wert for (int i=0;i<=n;i++) RichEdit1->Lines->Add(FloatToStr(ug+i*d)); } //----- Aufgabe 5.8.4.1-2 -------------------------------------------void __fastcall TForm1::Aufg1_2Click(TObject *Sender) { RichEdit1->Lines->LoadFromFile("Aufg584.rtf"); RichEdit1->Lines->Insert(0,"Wenn man die Datei Aufg584.rtf mit MS Word anzeigt,"); RichEdit1->Lines->Insert(1,"werden die Tabellen übersichtlicher dargestellt als "); RichEdit1->Lines->Insert(2,"mit dieser RichEdit-Komponente. "); RichEdit1->Lines->Insert(3,""); } //----- Aufgabe 5.8.4.3 ---------------------------------------------// FraktalU.cpp wurde dem Projekt hinzugefügt #include "FraktalU.h" void __fastcall TForm1::FraktaleClick(TObject *Sender) { FraktalForm->ShowModal(); } //----- Aufgabe 5.8.5, 1-4 ------------------------------------------void __fastcall TForm1::Aufg1_4Click(TObject *Sender) { RichEdit1->Lines->LoadFromFile("Aufg585.rtf"); RichEdit1->Lines->Insert(0,"Wenn man die Datei Aufg585.rtf mit MS Word anzeigt,"); RichEdit1->Lines->Insert(1,"werden die Tabellen übersichtlicher dargestellt als "); RichEdit1->Lines->Insert(2,"mit dieser RichEdit-Komponente. "); RichEdit1->Lines->Insert(3,""); } //----- Aufgabe 5.8.5.5 ---------------------------------------------// Hier mit 'AnsiString' - mit 'string' geht die Lösung analog AnsiString BigIntShiftLeft(AnsiString a) { return a+"0"; } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
AnsiString BigIntShiftRight(AnsiString a) { return a.Delete(a.Length(),1); } bool BigIntNull(AnsiString a) { // setzt voraus, dass 0 nicht durch "00" o. ä. dargestellt wird return (a=="0")||(a==""); } int LastDigit(AnsiString a) { // ergibt die letzte Ziffer eines Strings als Zahl return a[a.Length()]-'0'; } AnsiString ziffer[10]={"0","1","2","3","4","5","6","7","8","9"}; AnsiString BigIntMultByDigit(AnsiString a, int n) { // Multipliziert die String-Zahl a mit der int-Zahl n AnsiString s; int ue=0; for (int i=1; i<=a.Length(); i++) { int ai=a[a.Length()-i+1]-'0'; int r=(ai*n + ue)%10; ue=(ai*n + ue)/10; s=ziffer[r]+s; } if (ue>0) s=ziffer[ue]+s; return s; } int Max(int a, int b) { return (a>b)?a:b; } AnsiString BigIntAdd(AnsiString a, AnsiString b) { AnsiString s; int m=Max(a.Length(),b.Length()); int ue=0; for (int i=1; i<=m; i++) { int ai,bi; if (i<=a.Length()) ai=a[a.Length()-i+1]-'0'; else ai=0; if (i<=b.Length()) bi=b[b.Length()-i+1]-'0'; else bi=0; int r=(ai+bi+ue)%10; ue=(ai+bi+ue)/10; s=ziffer[r]+s; } if (ue>0) s="1"+s; return s; } AnsiString BigIntMult(AnsiString u, AnsiString v) { AnsiString z=""; // Null AnsiString x=u,y=v; // assert(z+x*y==u*v); while (!BigIntNull(x)) // (x != 0) { // if (x&1) z = z + y; z=BigIntAdd(z,BigIntMultByDigit(y,LastDigit(x))); y=BigIntShiftLeft(y); // y = y*2; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
235
236
12 Lösungen
x=BigIntShiftRight(x); // x = x/2; } // assert(z+x*y==u*v && x==0) return z; } AnsiString fakultaet(int n) { AnsiString result="1"; for (int i=2; i<=n; i++) result=BigIntMult(result,IntToStr(i)); return result; } double double_fakultaet(int n) { double result=1; for (int i=2; i<=n; i++) result=result*i; return result; } void __fastcall TForm1::StringzahlMultClick(TObject *Sender) { for (int i=1; i<=45; i++) { RichEdit1->Lines->Add(IntToStr(i)+"!="+fakultaet(i)); RichEdit1->Lines->Add(IntToStr(i)+"!="+double_fakultaet(i)); } } 12.4.8 Aufgaben 5.8.4 1. a) n <= 0 und n gerade. b) Fall I: Fall II:
i <= 0. Der Schleifenkörper wird nie ausgeführt, also erhält man auch keine Endlosschleife. i > 0. Da für i > 0 stets i/2 < i gilt, wird i bei jeder Ausführung des Schleifenkörpers verkleinert.
Deshalb erhält man für keinen Wert von i eine Endlosschleife. Dagegen würde die Schleifenbedingung (i >= 0) anstelle von (i > 0) zu einer Endlosschleife führen, da 0/2 = 0. c) Fall I: Fall II:
i >= n: Der Schleifenkörper wird nie ausgeführt. i < n: Für jeden Wert von i ist i + 1 > i.
Deshalb wird i bei jeder Ausführung vergrößert und erhält schließlich den Wert n. Auch diese Schleife wird also nie zur Endlosschleife. d) Bei einer Prüfung von Gleitkommawerten auf Gleichheit können Ungenauigkeiten in der Zahldarstellung dazu führen, dass eine erwartete Gleichheit doch nicht eintritt. Deshalb sollten Schleifen nie durch eine Prüfung von Gleitkommawerten auf Gleichheit kontrolliert werden. Eine Kontrolle der Schleife durch die Bedingung d >= 10 wäre allerdings relativ unproblematisch. Jedoch bietet auch diese Bedingung keine Sicherheit dafür, dass der Schleifenkörper so oft ausgeführt wird, wie man das gerne hätte. Es kann durchaus vorkommen, dass er einmal zu oft oder einmal zu wenig ausgeführt wird. e) Wird für i > 0 immer zur Endlosschleife, da die Kontrollvariable in der Schleife nicht verändert wird. f) siehe b).
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
237
Wie die Lösungen der Aufgaben b) und c) nahe legen, ist die endliche Wiederholung einer Schleife z.B. dann gesichert, wenn sie durch eine ganzzahlige Variable i kontrolliert wird, wobei entweder 1. die wiederholte Ausführung des Schleifenkörpers stets i >= n (bzw. i > n) voraussetzt und i bei jeder Wiederholung verkleinert wird oder 2. die wiederholte Ausführung des Schleifenkörpers stets i <= n (bzw. i < n) voraussetzt und i bei jeder Wiederholung vergrößert wird. Insbesondere sollte eine Schleife nie durch eine Prüfung von zwei Gleitkommawerten auf Gleichheit kontrolliert werden, da Ungenauigkeiten bei der Darstellung von Gleitkommawerten leicht zu Endlosschleifen führen. 2. 1. Mit den Regeln von de Morgan ergibt sich aus !((n > 0) && (c != 'q')) die Bedingung (n <= 0) || (c == 'q').
2. Die Bedingung (n <= 0) aus der Lösung von 1. lässt sich zu (n = 0) verschärfen.
12.4.9 Aufgabe 5.8.4.3
// Datei: d:\Loesungen\kap-5\5.8\FraktalU.cpp
#include "FraktalU.h" TFraktalForm *FraktalForm; __fastcall TFraktalForm::TFraktalForm(TComponent* Owner) : TForm(Owner) { } // Die folgenden beiden Variablen werden in FormCreate // bei mehr als 256 Bildschirmfarben bool moreThan256Colors=false; // auf true gesetzt bool usePalette=true; // auf false gesetzt // a) double double
x0= -2; y0= 1.25;
// links oben
double double
x1 = 0.5; // rechts unten y1 = -1.25;
// e) enum {Mandelbrot, Julia} Art_des_Fraktals=Mandelbrot; long double jx= -0.745, jy= 0.1125; // für die Julia-Mengen // c) bool aborted=false; // Damit die Schleife unterbrochen werden kann // b) TColor IndexColor(int i) { // return PALETTEINDEX (i%16); if (moreThan256Colors) { i=i%256; // maximal 255 Farben Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
238
12 Lösungen
int r30=i%30; // i->[0..29] int t=255-5*r30; // [0..29]->[255..145] if (i<30) return RGB(255-8*i,255,255); // weiss..türkis else if (i<60) return RGB(0,255-8*r30,255); // türkis..blau else if (i<90) return RGB(0,0,t); // blau .. dunkelblau else if (i<120) return RGB(0,t,0); // grün .. dunkelgrün else if (i<150) return RGB(t,0,0); // rot .. dunkelrot else if (i<180) return RGB(t,t,0); else if (i<210) return RGB(t,0,t); else if (i<240) return RGB(0,t,t); else return RGB(t,t,t); // Graustufen } else if (usePalette) return PALETTEINDEX (i); else { static TColor c[17]={clWhite, clLtGray, clGray, clDkGray, clGreen, clLime, clNavy, clOlive, clAqua, clBlue, clPurple, clRed, clFuchsia, clMaroon, clSilver, clTeal, clYellow}; return c[i%17]; } } // a) #include "\Loesungen\CppUtils\GraphUtils.cpp" void ZeichneFraktal(TImage* Image) { const int max_it = 250; // 50,100,200 aborted = false; // für b) for (int px=0; (px <= Image->Width-1) && (!aborted); px++) { for (int py=0;(py<=Image->Height-1) && (!aborted); py++) { double x = x_Welt(px,x0,x1,Image->Width); double y = y_Welt(py,y1,y0,Image->Height); long double x_it_alt = x; long double y_it_alt = y; long double dist; int i = 0; do { i++; /* Eigentlich müssen diese Berechnungen durchgeführt werden: x_it = x_it_alt*x_it_alt - y_it_alt*y_it_alt + x; y_it = 2*x_it_alt*y_it_alt + y; dist = x_it*x_it+y_it*y_it; Die folgenden Operationen sparen aber einige Multiplikationen: */ long double sqr_x = x_it_alt*x_it_alt; long double sqr_y = y_it_alt*y_it_alt; long double pr_xy = x_it_alt*y_it_alt; long double x_it,y_it; /* e) */ if (Art_des_Fraktals==Mandelbrot) { x_it = sqr_x - sqr_y + x; y_it = pr_xy + pr_xy + y; } else if (Art_des_Fraktals==Julia) { x_it = sqr_x - sqr_y + jx; y_it = pr_xy + pr_xy + jy; } dist = sqr_x + sqr_y; x_it_alt = x_it; y_it_alt = y_it; } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
//
239
while ((i <= max_it) && (dist <= 4)); TColor color=IndexColor(i); TColor color=IndexColor(py);// Zum Test der Farbverläufe if (i > max_it) color = clBlack; Image->Canvas->Pixels[px][py] = color;
} // c) Damit ein eventuelles Anklicken des Buttons "Abbruch" // erkannt wird und die Pixel gezeichnet werden. if (!usePalette && px%5==0) Application->ProcessMessages(); } int i=0; i++; return; } void __fastcall TFraktalForm::ZeichneClick(TObject *Sender) { FraktalForm->Caption="Fraktal: x0="+FloatToStr(x0)+" y0="+FloatToStr(y0)+ " x1="+FloatToStr(x1)+" y1="+FloatToStr(y1); // Form1->Width = 400; // Form1->Height = 400; // Form1->Image1->Align = alClient; HPALETTE hpal; if (usePalette) // nur bei 256 Farben { HPALETTE CreateMyPalette(); // Prototyp, Definition unten HPALETTE hpal=CreateMyPalette(); SelectPalette(Image1->Canvas->Handle,hpal,true); RealizePalette(Image1->Canvas->Handle); } ZeichneFraktal(Image1); if (usePalette) DeleteObject(hpal); FraktalForm->Image1->Picture->SaveToFile("c:\\Fraktal.bmp"); } // c) void __fastcall TFraktalForm::AbbruchClick(TObject *Sender) { // Da das Zeichnen eines Fraktals relativ lange dauern kann, // kann man es durch ein Anklicken dieses Buttons abbrechen. aborted=true; } // d) double x_start, y_start; // Position bei MouseDown void __fastcall TFraktalForm::Image1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { x_start = x_Welt(X,x0,x1,Image1->Width); y_start = y_Welt(Y,y1,y0,Image1->Height); } void __fastcall TFraktalForm::Image1MouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { x1 = x_Welt(X,x0,x1,Image1->Width); y1 = y_Welt(Y,y1,y0,Image1->Height); x0 = x_start; y0 = y_start; } // f) struct TMandelbrotParam { char* Name; // AnsiString Name; // geht nicht Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
240
12 Lösungen
double x0; double y0; double x1; double y1; }; const int nMBAW=13; TMandelbrotParam MBPar[nMBAW]= { // {AnsiString("Apfelmännchen"), -2, 1.25, 0.5, {"Apfelmännchen", -2, 1.25, 0.5, -1.25}, {"Seepferdchen", -0.88, 0.18, -0.70, 0.0}, {"Seepferdchen", -0.73206,0.1570,-0.73196,0.1569}, {"Apfelmännchen unten", -0.17, -1.02, -0.15, -1.04}, {"Apfelmännchen links",-1.80, 0.03, -1.74, -0.03}, {"Spirale", -0.750, 0.105, -0.740, 0.095} };
-1.25},
struct TJuliaParam { char* Name; // AnsiString Name; // geht nicht double x0; double y0; double x1; double y1; double jx; double jy; }; const int nJAW=9; TJuliaParam JPar[nJAW]= { // Bezeichnungen teilweise nach Peitgen u. a. (1986) { "Peitgen Fig. 15", -1.6, -1.2, 1.6, 1.2, -0.74543, 0.11301}, { "Peitgen Map 18", -1, -1.2, 1, 1.2, 0.32, 0.043}, { "Peitgen Map 20", -2, -1.5, 2, 1.5, -0.12375, 0.56508}, { "Siegel Disk", -2, -1.5, 2, 1.5, -0.39054, -0.58679}, { "Fatou Staub", -2, -1.5, 2, 1.5, 0.11031, -0.67037}, { "Zweig", -2, -1.5, 2, 1.5, 0, 1}, { "Zweig mit Perlen", -2, -1.5, 2, 1.5, -0.15652, 1.03225}, { "Julia 1", -2, -1.5, 2, 1.5, -0.72, 0.53}, { "Julia 2", -2, -1.5, 2, 1.5, -1.776, 0.4}, }; void __fastcall TFraktalForm::ListBox1Click(TObject *Sender) { Art_des_Fraktals=Mandelbrot; int i=ListBox1->ItemIndex; if (i<0) i=0; x0 = MBPar[i].x0; y0 = MBPar[i].y0; // links oben x1 = MBPar[i].x1; y1 = MBPar[i].y1; // rechts unten } void __fastcall TFraktalForm::ListBox2Click(TObject *Sender) { Art_des_Fraktals=Julia; int i=ListBox2->ItemIndex; if (i<0) i=0; x0 = JPar[i].x0; y0 = JPar[i].y0; x1 = JPar[i].x1; y1 = JPar[i].y1; jx = JPar[i].jx; jy = JPar[i].jy; } void __fastcall TFraktalForm::FormCreate(TObject *Sender) { Memo1->Lines->Clear(); Memo1->Lines->Add("In der Zeichnung kann ein " "rechteckiger Bereich mit der Maus " "markiert werden. Die linke obere " Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
241
"Ecke des Rechtecks erhält man beim " "Drücken und die rechte untere Ecke " "beim Loslassen der Maustaste. Mit " "dem Button 'Zeichne' wird dieser " "Ausschnitt gezeichnet (Zoom)."); int n=GetDeviceCaps(Image1->Canvas->Handle,NUMCOLORS); moreThan256Colors = (n==-1); // -1, falls Farbtiefe größer als 8 Bits (256 Farben) if (moreThan256Colors) usePalette=false; // f) for (int i=0; i< nMBAW; i++) ListBox1->Items->Add(MBPar[i].Name); for (int i=0; i< nJAW; i++) ListBox2->Items->Add(JPar[i].Name); } HPALETTE CreateMyPalette() { // Diese Funktion ist nur dann notwendig, wenn für den Bildschirm nur 256 // Farben eingestellt sind. Sie erzeugt eine eigene Farbpalette, damit // auch mit dieser Einstellung mehr als die ca. 20 Systemfarben verfügbar // sind. const int nColMyPalette=6*6*6; // Anzahl der Farben in der eigenene Palette: // Weniger als 256-20, damit bei einer Bildschirmeinstellung // mit 256 Farben die 20 Systemfarben nicht beeinflußt werden. PLOGPALETTE pPal = (PLOGPALETTE)LocalAlloc(LPTR, sizeof(LOGPALETTE) + nColMyPalette * sizeof(PALETTEENTRY)); // pPal = (COLORREF *)LocalAlloc(LPTR, sizeof(COLORREF) *(N+1)); if (!pPal) return(NULL); pPal->palVersion = 0x300; pPal->palNumEntries = nColMyPalette; int i=0; // Da schwarz eine spezielle Bedeutung hat, // hier nicht mit schwarz=(0,0,0) anfangen for (int r=1; r<=6; r++) // wie in nColMyPalette for (int g=1; g<=6; g++) // wie in nColMyPalette for (int b=1; b<=6; b++) // wie in nColMyPalette if (ipalPalEntry[i].peRed = r*nColMyPalette/6; pPal->palPalEntry[i].peGreen = g*nColMyPalette/6; pPal->palPalEntry[i].peBlue = b*nColMyPalette/6; pPal->palPalEntry[i].peFlags = 0; i++; } HPALETTE hLogPal = CreatePalette(pPal); LocalFree(pPal); return(hLogPal); }
12.4.10 Aufgaben 5.8.5
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
242
12 Lösungen
1. a) n = 4
f 1
f=1 for (int i=2;i<=n;i++) i=2
i
i<=n
2
i<=n f=f*i i++
1*2 = 2
i<=n f=f*i i++
3*3 = 6
i<=n f=f*i i++
6*4 = 24
true 3
true 4
true 5
i<=n
false 24
b) u = 3, v = 5
x=u y=v z=0 while (x!=0) x!=0 if (x&1) z=z+y y=y*2 x=x/2 x!=0 if (x&1) z=z+y y=y*2 x=x/2 x!=0
x 3
y
z
x!=0
5 0
true 0+5=5 2*5=10 3/2=1
true 5+10=15 2*10=20 1/2=0
false 0
20
15
c) u = 0, v = 5
x=u y=v z=0 while (x!=0)
x 0
y
z
x!=0
5 0 0
5
0
d) u = 3, v = 2
p=1 n=v x=u while (n>0) n>0 if (n&1) p=p*x n=n/2 x=x*x n>0 if (n&1) p=p*x n=n/2 x=x*x n>0
p 1
n
x
n > 0
2 3
true 1 3*3=9
true 1*9=9 0 9*9=81
false 9
0
81
2. In Aufgabe 5.7.3.5 wurde nachgewiesen, dass die Beziehung p*xn = uv
eine Invariante für diese Anweisungen ist. Diese Beziehung gilt wegen Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
243
p = 1; n = v; x = u;
auch vor der ersten Ausführung der while-Schleife. Da nach dem Verlassen der Schleife n=0 gilt, falls die Funktion mit n >= 0 aufgerufen wird, ist das Ergebnis der gesamten Anweisungsfolge p = uv.
Nach Aufgabe 5.8.4.1 b) wird diese Schleife auch nicht zu einer Endlosschleife, falls beim Aufruf der Schleife n >= 0 gilt. Deshalb wird diese Schleife immer verlassen, und anschließend gilt tatsächlich p = uv. 3. int ggt(int x, int y) { // x=x0, y=y0, ggT(x,y) = ggT(x0,y0) if (x<0) x=-x; // Damit die Funktion auch mit negativen if (y<0) y=-y; // Argumenten das richtige Ergebnis hat. // ggT(x,y) = ggT(x0,y0) while (y != 0) // Ablaufprot. für den Schleifenkörper {// x y r // ggT(x,y)=ggT(x0,y0) x0 y0 int r = x%y; // x0%y0 x = y; // y0 y = r; // x0%y0 // ggT(x,y) = ggT(y0,x0%y0) = ggT(x0,y0) } // ggT(x,y) = ggT(x0,y0) und y=0 return x; // ggT(x,y)==x; }
4. a) Am Anfang des Schleifenkörpers gilt nach den ersten Durchläufen der while-Schleife jeweils i = 1, s = 0, i = 2, s = p[n] i = 3, s = p[n]*x + p[n–1] i = 4, s = p[n]*x2 + p[n–1]*x + p[n–2]
Diese Beziehungen lassen die Schleifeninvariante s = p[n]*xi–2 + p[n–1]*xi–3 + ... + p[n–i+2]*x0
erwarten, die sich mit dem folgenden symbolischen Ablaufprotokoll auch nachweisen lässt. Dabei wurde io anstelle von i0 geschrieben, da ich – oder mein Textverarbeitungssystem – nicht doppelt tief indizieren kann: i io
s s0 = p[n]*xio–2 + p[n–1]*xio–3 + ... + p[n – io + 2]
s = s*x + p[n–i+1];
i++ ;
s0*x+p[n–io+1] = p[n]*xio–1 + p[n–1]*xio–2 + ... + p[n–io+2]*x + p[n–io+1]
io+1
p[n]*xio–2 + p[n–1]*xio–3 + ... + p[n–io+3]*x + p[n–io+2]
Damit gilt nach dem Verlassen der while-Schleife wegen i=n+2 tatsächlich die Behauptung.
Dass die Schleife auch verlassen wird, ergibt sich aus der Konstruktion der Schleife: Ausgehend von i = 1 wird i hochgezählt, bis i einen Wert größer als n hat. Diese Funktion entspricht den im C++Builder (aber nicht im C++-Standard) vordefinierten Funktionen (ohne die Ableitung) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
244
12 Lösungen
double poly(double x, int degree, double coeffs[]); bzw.
long double polyl(long double x, int degree, long double coeffs[]); b) const int n = 10; // Der Grad des Polynoms. double horner(double x, double* p, double& Ableitung) { double s = 0; int i = 1; Ableitung = 0; while (i <= n+1) { Ableitung = Ableitung*x + s; s = s*x + p[n-i+1]; i++; } return s; }
12.4.11 Aufgaben 5.10.8
// Datei: d:\Loesungen\kap-5\5.10\ExceptU.cpp
#include "ExceptU.h" //----- Aufgabe 1 -----------------------------------------------/* a) in keiner der Funktionen f1, f2 usw. eine Exception ausgelöst wird: Lösung: f1, f2, f4 b) in f1 eine Exception ausgelöst wird Lösung: f1, f3, f4 c) in f2 eine Exception ausgelöst wird Lösung: f1, f2, f3, f4 d) in f1 und f3 eine Exception ausgelöst wird Lösung: f1, f3, f5 e) in f1 und f4 eine Exception ausgelöst wird Lösung: f1, f3, f4, f5 */ void f1(char c) { Form1->Memo1->Lines->Add("f1"); if (c=='b') throw 1; if (c=='d') throw 1; if (c=='e') throw 1; }; void f2(char c) { Form1->Memo1->Lines->Add("f2"); if (c=='c') throw 2; }; void f3(char c) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
{ Form1->Memo1->Lines->Add("f3"); if (c=='d') throw 3; }; void f4(char c) { Form1->Memo1->Lines->Add("f4"); if (c=='e') throw 4; }; void f5(char c) { Form1->Memo1->Lines->Add("f5"); }; void f_(char c) { Form1->Memo1->Lines->Add(c); try { try { f1(c); f2(c); } catch(...) { f3(c); } f4(c); } catch (...) { f5(c); } } void __fastcall TForm1::Aufg1Click(TObject *Sender) { f_('a'); f_('b'); f_('c'); f_('d'); f_('e'); } //----- Aufgabe 2 -----------------------------------------------#include <exception> #include <stdexcept> // für logic_error using namespace std; void Aufg2(int i) { if (i==0) throw i+1; else if (i==1) throw 'A'; else if (i==2) throw 1.0*i; else if (i==3) throw "Hallo"; else if (i==3) throw exception(); else throw logic_error("Vorbedingung nicht erfüllt"); } void test_Aufg2(int i) { try { Aufg2(i); } catch(int i) { Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
245
246
Form1->Memo1->Lines->Add("int Exception i="+IntToStr(i)); } catch(char i) { Form1->Memo1->Lines->Add("char Exception i="+IntToStr(i)); } catch(double i) { Form1->Memo1->Lines->Add("double Exception i="+FloatToStr(i)); } catch(char* i) { Form1->Memo1->Lines->Add(AnsiString("char* Exception i=")+i); } catch(logic_error& i) { Form1->Memo1->Lines->Add(AnsiString("Exception i=")+i.what()); } catch(exception& i) { Form1->Memo1->Lines->Add(AnsiString("Exception i=")+i.what()); } }; void __fastcall TForm1::Aufg2Click(TObject *Sender) { test_Aufg2(0); test_Aufg2(1); test_Aufg2(2); test_Aufg2(3); test_Aufg2(4); test_Aufg2(5); } //----- Aufgabe 3 -----------------------------------------------#include <stdexcept> void f(int i) { if (i==0) throw exception(); else if (i==1) throw logic_error("logic error"); else if (i==2) throw range_error("range error"); else if (i==3) throw out_of_range("out of range error"); else throw exception(); } // a) void g1(int i) { try { f(i); } catch(logic_error& e) { Form1->Memo1->Lines->Add("logisch"); } catch(out_of_range& e) { Form1->Memo1->Lines->Add("range"); } catch(exception& e) { Form1->Memo1->Lines->Add("exception"); } }; void test_Aufg3a() { g1(0); // exception g1(1); // logisch g1(2); // exception g1(3); // logisch } // b) void g2(int i) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.4 Lösungen Kapitel 5
247
{ try { f(i); } catch(logic_error& e) { Form1->Memo1->Lines->Add(e.what()); } catch(out_of_range& e) { Form1->Memo1->Lines->Add(e.what()); } catch(exception& e) { Form1->Memo1->Lines->Add(e.what()); } }; void test_Aufg3b() { g2(0); // exception::what(), "no named exception thrown" g2(1); // logic_error::what(), "logic error" g2(2); // range_error::what(), "range error" g2(3); // out_of_range::what(), "out of range error" // Dieses Ergebnis erhält man einfacher wie in d) } // c) void g3(int i) { try { f(i); } catch(exception e) { Form1->Memo1->Lines->Add(e.what()); } }; void test_Aufg3c() { g3(0); // exception::what(), "no named exception thrown" g3(1); // exception::what(), "no named exception thrown" g3(2); // exception::what(), "no named exception thrown" g3(3); // exception::what(), "no named exception thrown" // Da der Datentyp im Exception-Handler keine Referenz // ist, wird immer die Funktion 'what' von 'exception' // aufgerufen. } // d) void g4(int i) { try { f(i); } catch(exception& e) { Form1->Memo1->Lines->Add(e.what()); } catch(...) { Form1->Memo1->Lines->Add("irgendeine Exception"); } }; void test_Aufg3d() { g4(0); // exception::what(), g4(1); // logic_error::what(), g4(2); // range_error::what(), g4(3); // out_of_range::what(),
"no named exception thrown" "logic error" "range error" "out of range error"
} void __fastcall TForm1::Aufg3Click(TObject *Sender) { test_Aufg3a(); test_Aufg3b(); test_Aufg3c(); test_Aufg3d(); } //----- Aufgabe 4 -----------------------------------------------#include <sstream> Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
248
12 Lösungen
unsigned int except_flags=ios::failbit; double StrToFloat(string s) { istringstream is(s); is.exceptions(except_flags); double d; is>>d; return d; } int StrToInt(string s) { istringstream is(s); is.exceptions(ios::failbit|ios::badbit); int i; is>>i; return i; } void test_StrToInt1(string str) { string s; int i=-17; try { // i=StrToInt(str); i=StrToFloat(str); } catch(exception& e) { s=e.what(); } catch(...) { s="StrToError"; } Form1->Memo1->Lines->Add(s.c_str()+AnsiString(": ")+IntToStr(i)); } void test_StrToFloat2(unsigned flag) { Form1->Memo1->Lines->Add(" ##"+IntToStr(flag)); except_flags=flag; test_StrToInt1("123"); test_StrToInt1(" 123 "); test_StrToInt1("1 23"); test_StrToInt1("12A3"); test_StrToInt1("123A"); test_StrToInt1(""); } void __fastcall TForm1::Aufg4Click(TObject *Sender) { // Mit IntToStr: test_StrToFloat2(0); // 123 123 1 12 test_StrToFloat2(ios::failbit); // 123 123 1 12 test_StrToFloat2(ios::eofbit); // 123 123 1 12 test_StrToFloat2(ios::badbit); // 123 123 1 12
123
failbit
123
failbit
123
failbit
123
failbit
123
0
123
failbit
// Mit FloatToStr: test_StrToFloat2(0); // 123 123 1 12 test_StrToFloat2(ios::failbit); // 123 123 1 12 test_StrToFloat2(ios::eofbit);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
// eofbit 123 1 12 test_StrToFloat2(ios::badbit); // 123 123 1 12
249
123
eofbit
123
0
} //----- Aufgabe 5 -----------------------------------------------class my_exception:public exception { string str; public: my_exception(const string& msg):str(msg) { const char* what() const { return str.c_str(); } };
};
void f() { throw my_exception("ich war's"); } void Aufgabe5() { try { f(); } catch (exception& e) { // fängt alle von exception abgeleiteten Exceptions ab ShowMessage(e.what()); } } void __fastcall TForm1::Aufg5Click(TObject *Sender) { Aufgabe5(); } //----- Aufgabe 6 -----------------------------------------------class so_nicht:public exception { char* str; public: so_nicht(char* msg):str(msg) { }; const char* what() const { return str; } }; void f1() { char s[]="ich war's"; // existiert nach dem Verlassen von f1 nicht mehr throw so_nicht(s); } void Aufgabe6() { try { f1(); } catch (exception& e) { // fängt alle von exception abgeleiteten Exceptions ab ShowMessage(e.what()); } } void __fastcall TForm1::Aufg6Click(TObject *Sender) { Aufgabe6(); } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
250
12 Lösungen
//----- Aufgabe 7 -----------------------------------------------// Siehe auch Betrand Meyer, Object Oriented Software Construction, // Abschnitt 12.2 class ENegative {}; double Sqrt(double d) { try { if (d<0) throw ENegative(); return sqrt(d); } catch(...) { ShowMessage("negativ"); return 0; } } void __fastcall TForm1::Aufg7Click(TObject *Sender) { double d=Sqrt(1)+Sqrt(-1); Form1->Memo1->Lines->Add(FloatToStr(d)); } //----- Aufgabe 8 -----------------------------------------------struct CDatum { int Tag; int Monat; int Jahr; }; CDatum StrToCDatum(AnsiString s) { // Das Datum kann man wie in Aufgabe 4.1.1 konvertieren oder so: // Ende Datum TDateTime d; d = StrToDate(s); unsigned short year, month, day; d.DecodeDate(&year, &month, &day); CDatum dat={day,month,year}; return dat; } struct CKontobewegung { int KontoNr; AnsiString NameInhaber; CDatum Datum; char BewArt; Currency Betrag; }; #include vector v; CKontobewegung getData1(TForm1* Form) { // TForm1 soll ein Formular sein, das die Edit-Felder EKontoNr->Text usw. enthält // Exceptions nicht abfangen, nur weitergeben CKontobewegung k; k.KontoNr=StrToInt(Form->EKontoNr->Text); k.NameInhaber=Form->EName->Text; k.Datum=StrToCDatum(Form->EDatum->Text.c_str()); Form->EBewart->Text.Trim(); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
if (Form->EBewart->Text.Length() >= 1) k.BewArt =Form1->EBewart->Text[1]; // sehr einfach k.Betrag = StrToCurr(Form->EBetrag->Text); return k; } void saveData1(TForm1* Form) { try { CKontobewegung k; k=getData1(Form); v.push_back(k); } catch(...) { ShowMessage("Fehler"); } }
CKontobewegung getData2(TForm1* Form) { // Alle Exceptions in einem einzigen try abfangen: // schlecht, da undefinierte Werte zurückgegeben werden können CKontobewegung k; try { k.KontoNr=StrToInt(Form->EKontoNr->Text); k.NameInhaber=Form->EName->Text; k.Datum=StrToCDatum(Form->EDatum->Text.c_str()); Form->EBewart->Text.Trim(); if (Form->EBewart->Text.Length() >= 1) k.BewArt =Form1->EBewart->Text[1]; // sehr einfach k.Betrag = StrToCurr(Form->EBetrag->Text); } catch(...) { ShowMessage("Fehler"); } return k; } void saveData2(TForm1* Form) { try { CKontobewegung k; k=getData2(Form1); v.push_back(k); } catch(...) { ShowMessage("Fehler"); } } CKontobewegung getData3(TForm1* Form) { // Jede Exceptions in einem eigenen try abfangen CKontobewegung k; Form->EBewart->Text.Trim(); if (Form->EBewart->Text.Length() >= 1) k.BewArt =Form1->EBewart->Text[1]; // sehr einfach k.Betrag = StrToCurr(Form->EBetrag->Text); try { k.KontoNr =StrToInt(Form->EKontoNr->Text); } catch(...) { Form1->EKontoNr->SetFocus(); throw; // } k.NameInhaber=Form->EName->Text; // müsste immer gehen try { k.Datum=StrToCDatum(Form->EDatum->Text.c_str()); } catch(...) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
251
252
{ Form1->EDatum->SetFocus(); throw; } return k; } // kann mit saveData1 verwendet werden void saveData3(TForm1* Form) { try { CKontobewegung k; k=getData3(Form); v.push_back(k); } catch(...) { ShowMessage("Fehler"); } } void __fastcall TForm1::Aufg8Click(TObject *Sender) { saveData1(Form1); } //----- Aufgabe 9 -----------------------------------------------void h1() { // kann zu einem Speicherleck führen vector v; int* p=new int(17); v.push_back(p); f(); for (vector::iterator i=v.begin(); i!=v.end(); i++) delete i; } class C { vector v; public: void push_back(int* p) { v.push_back(p); } ~C() { for (vector::iterator i=v.begin(); i!=v.end(); i++) delete i; } }; void h2() { C v; v.push_back(new int(17)); f(); } void __fastcall TForm1::Aufg9Click(TObject *Sender) { h1(); h2(); }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.5 Lösungen Kapitel 6
253
12.5 Lösungen Kapitel 6
12.5.1 Aufgabe 6.3.5 und 6.3.7
// Datei: d:\Loesungen\kap-6\6.3\FktParmU.cpp
#include "FktParmU.h" //----- Aufgaben 6.3.5 ----------------------------------------------//----- Aufgabe 1 -----------------------------------------------int a=0; void Init1() { a=1; // globale Variable } void call_Init1() { Init1; // Ohne den Aufrufoperator ist das kein Funktionsaufruf. Richtig wäre: // Init1(); } void __fastcall TForm1::Aufg6351Click(TObject *Sender) { call_Init1(); } //----- Aufgabe 2 -----------------------------------------------char* Nachkommastellen(char *s) { char w[200]; strcpy(w, s); return strstr(w, ",")+1; // Diese Funktion gibt einen Zeiger auf eine Variable auf dem Stack zurück, // die nach dem Aufruf nicht mehr existiert. Mit return strstr(s, ",")+1; // erhält man das erwartete Ergebnis. } void __fastcall TForm1::Aufg6352Click(TObject *Sender) { Memo1->Lines->Add(Nachkommastellen("0,123")); Memo1->Lines->Add(Nachkommastellen("1,23")); Memo1->Lines->Add(Nachkommastellen("12,3")); } //----- Aufgabe 3 -----------------------------------------------void __fastcall TForm1::Aufg6353Click(TObject *Sender) { Memo1->Lines->Text=Memo1->Lines->Text+ "\nBei der Parameterübergabe im Stil von C müssen in der " "Funktionsdefinition alle Verwendungen des Parameters dereferenziert werden. Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
"
254
12 Lösungen
"In allen Aufrufen der Funktion muss der Parameter durch seine Adresse " "ersetzt werden. Bei Referenzparametern muss dagegen nur der Datentyp in " "der Funktionsdefinition in einen Referenzparameter geändert werden. \n"; } //----- Aufgabe 4 -----------------------------------------------void Aendere_Zeiger_ptr(int** p) { (*p)=0; } void Aendere_Zeiger_ref(int*& p) { p=0; } void __fastcall TForm1::Aufg6354Click(TObject *Sender) { int i=17,j=18; int* p=&i; Aendere_Zeiger_ptr(&p); int* q=&j; Aendere_Zeiger_ref(q); } //----- Aufgabe 5 -----------------------------------------------void zero(void* p, int n) { for (int i=0; i
12.5 Lösungen Kapitel 6
{ { // a) wenn x den Datentyp int hat. int x; x = 0; int y1=f6(x)+g6(x); x = 0; int y2=g6(x)+f6(x); Memo1->Lines->Add("y1="+IntToStr(y1)+" y2="+IntToStr(y2)); // Da hier das Argument denselben Datentyp wie der Parameter hat, werden // die Anweisungen in den Funktionen mit dem Argument durchgeführt, was // einen Seiteneffekt zur Folge hat. } { // b) wenn x den Datentyp double hat. double x; x = 0; int y1=f6(x)+g6(x); x = 0; int y2=g6(x)+f6(x); Memo1->Lines->Add("y1="+IntToStr(y1)+" y2="+IntToStr(y2)); // In b) und c) hat das Argument einen anderen Datentyp als der // Parameter. Deswegen werden die Anweisungen in den Funktionen nicht // mit dem Argument durchgeführt, sondern mit einer temporären // Variablen. Deshalb haben diese Funktionen hier keinen Seiteneffekt. } { // c) wenn x den Datentyp char hat. char x; x = 0; int y1=f6(x)+g6(x); x = 0; int y2=g6(x)+f6(x); Memo1->Lines->Add("y1="+IntToStr(y1)+" y2="+IntToStr(y2)); // siehe b) } }
//----- Aufgaben 6.3.8 ----------------------------------------------//----- Aufgabe 1 -----------------------------------------------struct C2DPunkt { int x, y; }; C2DPunkt InitPunkt(int x=0, int y=0) { C2DPunkt p={x,y}; return p; } void __fastcall TForm1::AInitPunktClick(TObject *Sender) { C2DPunkt p1, p2, p3; p1=InitPunkt(); p2=InitPunkt(1); p3=InitPunkt(2,3); } //----- Aufgabe 2 -----------------------------------------------void f1(void) {}; int f2(int,int) {return 0;}; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
255
256
12 Lösungen
double f3(double) {return 0;}; char* f4(char*) {return "";}; // a1) typedef typedef typedef typedef F1 F2 F3 F4
void (*F1)(); int (*F2)(int,int); double (*F3)(double); char* (*F4)(char*);
A1[10]; A2[10]; A3[10]; A4[10];
// a2) void int double char*
(*a1[10])(void); (*a2[10])(int,int); (*a3[10])(double); (*a4[10])(char*);
// b) void test() { a1[0]=f1; a1[0](); a2[0]=f2; a2[0](1,1); a3[0]=f3; a3[0](1); a4[0]=f4; a4[0](""); A1[0]=f1; A1[0](); A2[0]=f2; A2[0](1,1); A3[0]=f3; A3[0](1); A4[0]=f4; A4[0](""); } // c) #include void __fastcall TForm1::FktTypenClick(TObject *Sender) { test(); Form1->Memo1->Lines->Add(typeid(F1).name()); Form1->Memo1->Lines->Add(typeid(F2).name()); Form1->Memo1->Lines->Add(typeid(F3).name()); Form1->Memo1->Lines->Add(typeid(F4).name()); } //----- Aufgabe 3 -----------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
typedef double(*real_func)(double) ; #include <math> #include // für numeric_limits<double>::epsilon // a) Für die Ableitung wird ein Näherungswert berechnet. void Newton_Nullstelle(double x0, real_func f, int& i, double& x) { const double eps=std::numeric_limits<double>::epsilon()*100; // epsilon ist der Unterschied zwischen 1 und der nächstgrößeren // double-Zahl. // const double eps=1E-12 kann ebenso anstelle von epsilon verwendet werden // i zählt die Iterationen - zur Vermeidung von Endlosschleifen // Abl: Näherungswert für die Ableitung bei x0 double Abl, h = 0.00001; // für die Berechnung der Ableitung: möglichst // klein, aber doch so groß, daß f(x+h)<>f(x) i = 0; x = x0; do { x0=x; // Startwert für die nächste Iteration i++; Abl = (f(x0 + h) - f(x0))/h; x = x0 - f(x0)/Abl; } while ((fabs(x-x0) >= eps) && (i <= 100)); }; // b) Die Ableitung wird ebenfalls als Funktion übergeben. void Newton_Nullstelle_mit_Abl(double x0, real_func f, real_func Abl, int& i, double& x) { const double eps=std::numeric_limits<double>::epsilon()*100; // const double eps=1E-12; // i zählt die Iterationen - zur Vermeidung von Endlosschleifen i = 0; x = x0; do { x0 =x; // Startwert für die nächste Iteration i++; x = x0 - f(x0)/Abl(x0); } while ((fabs(x-x0) >= eps) && (i <= 100)); } // c) Testen mit der Funktion f(x) = x^2–r und Vergleich // der Ergebnisse mit sqrt(r). int r; // damit r an die Quadratfunktion übergeben werden // kann, ohne die Parameterliste zu ändern - nicht schön double quadrat(double x) { return x*x - r; } double quadrat_abl(double x) { return 2*x; } void Teste_Newton1() { for (r=1; r<=100; r++) { int i; double x; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
257
258
12 Lösungen
Newton_Nullstelle(r, quadrat, i, x); Form1->Memo1->Lines->Add(Format("r=%g it=%d newt=%g sqrt=%g d=%g", OPENARRAY(TVarRec,(1.0*r,i,x,sqrt(r),fabs(x-sqrt(r)))))); Newton_Nullstelle_mit_Abl(r, quadrat,quadrat_abl, i, x); Form1->Memo1->Lines->Add(Format("r=%g it=%d newt=%g sqrt=%g d=%g", OPENARRAY(TVarRec,(1.0*r,i,x,sqrt(r),fabs(x-sqrt(r)))))); }; } void __fastcall TForm1::NewtonClick(TObject *Sender) { Teste_Newton1(); } //----- Aufgabe 4 -----------------------------------------------// a) double Trapezsumme(real_func f, double a, double b, int n) { double s = (f(a) + f(b))/2; double h = (b-a)/n; for (int i=1; i eps) { x_alt = x; n = 2*n; x = I(f,a,b,n); }; return x; }; // c) Testprogramm für die Integrationsverfahren: double q2,q1,q0; double quadratPoly(double x) { return q2*x*x + q1*x + q0; } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
double quadrat_Stamm(double x) { return q2*x*x*x/3 + q1*x*x/2 + q0*x; } void Teste_Integration() { q2 = 1; q1 = 0; q0 = 0; for (int i=-10; i<=10; i++) for (int j=i+1; j<=10;j++) { double a=i; double b=j; double t=iterate(a,b,quadratPoly,Trapezsumme); double s=iterate(a,b,quadrat,Simpsonsumme); double x=quadrat_Stamm(b) - quadrat_Stamm(a); Form1->Memo1->Lines->Add(Format("t=%g s=%g x=%g", OPENARRAY(TVarRec,(t,s,x)))); Application->ProcessMessages(); } }; void __fastcall TForm1::NumIntClick(TObject *Sender) { double t=Trapezsumme(f,0,1,10000); Memo1->Lines->Add("T="+FloatToStr(4*t)); double s=Simpsonsumme(f,0,1,10000); Memo1->Lines->Add("S="+FloatToStr(4*s)); Teste_Integration(); } //----- Aufgabe 5 -----------------------------------------------#include "\Loesungen\CppUtils\GraphUtils.cpp" void PlotFunction(double x0, double x1, real_func f, TImage* Image1) { // a) das alte Bild löschen: Image1->Canvas->Brush->Color = clWhite; Image1->Canvas->Rectangle(0,0,Image1->Width,Image1->Height); // b) bestimme den minimalen und maximalen Wert von f(x) im Bereich [x0,x1]: double y0=f(x_Welt(0, x0, x1, Image1->Width)); // y0=min(f(x)) double y1=y0; // y1=max(f(x)) for (int px=1; px<=Form1->Image1->Width-/*–*/1; px++) { // transformiere px in Welt-Koordinaten double x=x_Welt(px, x0, x1, Image1->Width); double y=f(x); if (yy1) y1=y; }; // zeichne die Kurve: Image1->Canvas->Pen->Color = clRed; for (int px=0; px<=Form1->Image1->Width-/*–*/1; px++) { // transformiere px in Welt-Koordinaten double x=x_Welt(px, x0, x1, Image1->Width); // transformiere y in Bildkoordinaten int py=y_Bildschirm(f(x), y0, y1, Image1->Height); if (px == 0) Form1->Image1->Canvas->MoveTo(px,py); else Form1->Image1->Canvas->LineTo(px,py); } // c) zeichne das Gitternetz: double dx=(x1-x0)/10; // 10 Gitterlinien double dy=(y1-y0)/10; // 10 Gitterlinien Gitternetz(Image1, x0,x1,dx, Image1->ClientWidth, y0,y1,dy, Image1->ClientHeight); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
259
260
12 Lösungen
} // d) const int max_n_functions=10; real_func pf=sin; real_func f_arr[max_n_functions]; void __fastcall TForm1::PlotFunctionClick(TObject *Sender) { ::PlotFunction(-10, 10, pf, Image1); } void __fastcall TForm1::FormCreate(TObject *Sender) { ListBox1->Items->Add("sin"); f_arr[0]=sin; ListBox1->Items->Add("cos"); f_arr[1]=cos; ListBox1->Items->Add("exp"); f_arr[2]=exp; } void __fastcall TForm1::ListBox1Click(TObject *Sender) { pf=f_arr[ListBox1->ItemIndex]; }
12.5.2 Aufgabe 6.3.8.6
// Datei: d:\Loesungen\kap-6\6.3\Fraktal2U.cpp
#include "Fraktal2U.h" #include // for
_control87
//----- Aufgabe 6.3.8.6 ---------------------------------------------// Die folgenden beiden Variablen werden in FormCreate // bei mehr als 256 Bildschirmfarben bool moreThan256Colors=false; // auf true gesetzt bool usePalette=true; // auf false gesetzt // a) double double
x0= -2; y0= 1.25;
double double
x1 = 0.5; // rechts unten y1 = -1.25;
enum
// links oben
{Mandelbrot, Julia} Art_des_Fraktals=Mandelbrot;
long double jx= -0.745, jy= 0.1125; // für die Julia-Mengen bool aborted=false; // b) Damit die Schleife unterbrochen werden kann TColor IndexColor(int i) { // return PALETTEINDEX (i%16); if (moreThan256Colors) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
{ i=i%256; // maximal 255 Farben int r30=i%30; // i->[0..29] int t=255-5*r30; // [0..29]->[255..145] if (i<30) return RGB(255-8*i,255,255); // weiss..türkis else if (i<60) return RGB(0,255-8*r30,255); // türkis..blau else if (i<90) return RGB(0,0,t); // blau .. dunkelblau else if (i<120) return RGB(0,t,0); // grün .. dunkelgrün else if (i<150) return RGB(t,0,0); // rot .. dunkelrot else if (i<180) return RGB(t,t,0); else if (i<210) return RGB(t,0,t); else if (i<240) return RGB(0,t,t); else return RGB(t,t,t); // Graustufen } else if (usePalette) return PALETTEINDEX (i); else { static TColor c[17]={clWhite, clLtGray, clGray, clDkGray, clGreen, clLime, clNavy, clOlive, clAqua, clBlue, clPurple, clRed, clFuchsia, clMaroon, clSilver, clTeal, clYellow}; return c[i%17]; } } #include "\Loesungen\CppUtils\GraphUtils.cpp" void ZeichneFraktal(TImage* Image) { const int max_it = 200; // 50,100,200 aborted = false; // für b) for (int px=0; (px <= Image->Width-1) && (!aborted); px++) { for (int py=0;(py<=Image->Height-1) && (!aborted); py++) { double x = x_Welt(px,x0,x1,Image->Width); double y = y_Welt(py,y1,y0,Image->Height); long double x_it_alt = x; long double y_it_alt = y; long double dist; int i = 0; do { i++; /* Eigentlich müssen diese Berechnungen durchgeführt werden: x_it = x_it_alt*x_it_alt - y_it_alt*y_it_alt + x; y_it = 2*x_it_alt*y_it_alt + y; dist = x_it*x_it+y_it*y_it; Die folgenden Operationen sparen aber einige Multiplikationen: */ long double sqr_x = x_it_alt*x_it_alt; long double sqr_y = y_it_alt*y_it_alt; long double pr_xy = x_it_alt*y_it_alt; long double x_it,y_it; if (Art_des_Fraktals==Mandelbrot) { x_it = sqr_x - sqr_y + x; y_it = pr_xy + pr_xy + y; } else if (Art_des_Fraktals==Julia) { x_it = sqr_x - sqr_y + jx; y_it = pr_xy + pr_xy + jy; } dist = sqr_x + sqr_y; x_it_alt = x_it; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
261
262
12 Lösungen
y_it_alt = y_it; } while ((i <= max_it) && (dist <= 4)); TColor color=IndexColor(i); if (i > max_it) color = clBlack; Image->Canvas->Pixels[px][py] = color; } // Damit ein eventuelles Anklicken des Buttons "Abbruch" erkannt wird // und die Pixel gezeichnet werden. if (!usePalette && px%5==0) Application->ProcessMessages(); } } void __fastcall TForm1::AbbruchClick(TObject *Sender) { // Da das Zeichnen eines Fraktals relativ lange dauern kann, // kann man es durch ein Anklicken dieses Buttons abbrechen. aborted=true; } // c) double x_start, y_start; // Position bei MouseDown void __fastcall TForm1::Image1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { x_start = x_Welt(X,x0,x1,Image1->Width); y_start = y_Welt(Y,y1,y0,Image1->Height); } void __fastcall TForm1::Image1MouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { x1 = x_Welt(X,x0,x1,Image1->Width); y1 = y_Welt(Y,y1,y0,Image1->Height); x0 = x_start; y0 = y_start; } // f) struct TMandelbrotParam { char* Name; double x0; double y0; double x1; double y1; }; const int nMBAW=13; TMandelbrotParam MBPar[nMBAW]= { {"Apfelmännchen", -2, 1.25, 0.5, -1.25}, {"Seepferdchen", -0.88, 0.18, -0.70, 0.0}, {"Seepferdchen", -0.73206,0.1570,-0.73196,0.1569}, {"Apfelmännchen unten", -0.17, -1.02, -0.15, -1.04}, {"Apfelmännchen links",-1.80, 0.03, -1.74, -0.03}, {"Spirale", -0.750, 0.105, -0.740, 0.095} }; struct TJuliaParam { char* Name; double x0; double y0; double x1; double y1; double jx; double jy; }; const int nJAW=9; TJuliaParam JPar[nJAW]= Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
{ // Bezeichnungen teilweise nach Peitgen u. a. (1986) { "Peitgen Fig. 15", -1.6, -1.2, 1.6, 1.2, -0.74543, 0.11301}, { "Peitgen Map 18", -1, -1.2, 1, 1.2, 0.32, 0.043}, { "Peitgen Map 20", -2, -1.5, 2, 1.5, -0.12375, 0.56508}, { "Siegel Disk", -2, -1.5, 2, 1.5, -0.39054, -0.58679}, { "Fatou Staub", -2, -1.5, 2, 1.5, 0.11031, -0.67037}, { "Zweig", -2, -1.5, 2, 1.5, 0, 1}, { "Zweig mit Perlen", -2, -1.5, 2, 1.5, -0.15652, 1.03225}, { "Julia 6", -2, -1.5, 2, 1.5, -0.72, 0.53}, { "Julia 7", -2, -1.5, 2, 1.5, -1.776, 0.4}, }; void __fastcall TForm1::ListBox1Click(TObject *Sender) { Art_des_Fraktals=Mandelbrot; int i=ListBox1->ItemIndex; if (i<0) i=0; x0 = MBPar[i].x0; y0 = MBPar[i].y0; // links oben x1 = MBPar[i].x1; y1 = MBPar[i].y1; // rechts unten } void __fastcall TForm1::ListBox2Click(TObject *Sender) { Art_des_Fraktals=Julia; int i=ListBox2->ItemIndex; if (i<0) i=0; x0 = JPar[i].x0; y0 = JPar[i].y0; x1 = JPar[i].x1; y1 = JPar[i].y1; jx = JPar[i].jx; jy = JPar[i].jy; } HPALETTE CreateMyPalette() { // Diese Funktion ist nur dann notwendig, wenn für den Bildschirm nur 256 // Farben eingestellt sind. Sie erzeugt eine eigene Farbpalette, damit auch // mit dieser Einstellung mehr als die ca. 20 Systemfarben verfügbar sind. const int nColMyPalette=6*6*6; // Anzahl der Farben in der eigenene Palette: // Weniger als 256-20, damit bei einer Bildschirmeinstellung // mit 256 Farben die 20 Systemfarben nicht beeinflußt werden. PLOGPALETTE pPal = (PLOGPALETTE)LocalAlloc(LPTR, sizeof(LOGPALETTE) + nColMyPalette * sizeof(PALETTEENTRY)); // pPal = (COLORREF *)LocalAlloc(LPTR, sizeof(COLORREF) *(N+1)); if (!pPal) return(NULL); pPal->palVersion = 0x300; pPal->palNumEntries = nColMyPalette; int i=0; // Da schwarz eine spezielle Bedeutung hat, // hier nicht mit schwarz=(0,0,0) anfangen for (int r=1; r<=6; r++) // wie in nColMyPalette for (int g=1; g<=6; g++) // wie in nColMyPalette for (int b=1; b<=6; b++) // wie in nColMyPalette if (ipalPalEntry[i].peRed = r*nColMyPalette/6; pPal->palPalEntry[i].peGreen = g*nColMyPalette/6; pPal->palPalEntry[i].peBlue = b*nColMyPalette/6; pPal->palPalEntry[i].peFlags = 0; i++; } HPALETTE hLogPal = CreatePalette(pPal); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
263
264
12 Lösungen
LocalFree(pPal); return(hLogPal); } void __fastcall TForm1::CountColorsClick(TObject *Sender) { const int max_col=257; int a[max_col]; int s=0; for (int i=0; i<max_col; i++) a[i]=0; for (int px=0; (px <= Form1->Image1->Width-1) ; px++) { for (int py=0;(py<=Form1->Image1->Height-1) ; py++) { int col=Image1->Canvas->Pixels[px][py]; bool gefunden=false; for (int j=0; j<s && !gefunden; j++) if (a[j]==col) gefunden=true; if (!gefunden) { if (0<=s && s<max_col-1) a[s]=col; s++; Application->ProcessMessages(); CountColors->Caption=IntToStr(s)+" "+IntToStr(px); } } } int n=0; for (int i=0; i<max_col; i++) if (a[i]>0) n++; CountColors->Caption=IntToStr(n); } // 6. a) #include using namespace std; typedef complex<double> Complex; typedef Complex (*FComplex)(const Complex&); void ZeichneFraktalComplex(TImage* Image, FComplex f) { const int max_it = 200; // 50,100,200 aborted = false; // für b) for (int px=0; (px <= Image->Width-1) && (!aborted); px++) { for (int py=0;(py<=Image->Height-1) && (!aborted); py++) { Complex z(x_Welt(px,x0,x1,Image->Width), y_Welt(py,y1,y0,Image->Height)); Complex // z_it_alt=z, z_it=z; int i = 0; do { i++; if (Art_des_Fraktals==Mandelbrot) z_it=f(z_it)+z; else if (Art_des_Fraktals==Julia) z_it=f(z_it)+Complex(jx,jy); } while ((i <= max_it) && (norm(z_it)<= 4)); TColor color=IndexColor(i); if (i > max_it) color = clBlack; Image->Canvas->Pixels[px][py] = color; } // Damit ein eventuelles Anklicken des Buttons "Abbruch" erkannt wird // und die Pixel gezeichnet werden. Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
265
if (!usePalette && px%5==0) Application->ProcessMessages(); } } Complex CFQuadr(const Complex& z) Complex CFQuadr1(const Complex& z) Complex CFQuadr2(const Complex& z) Complex CFKubik(const Complex& z) Complex CFxxexp(const Complex& z) Complex CFNewtKubik(const Complex& 1.0)/(3.0*z*z+0.5*Complex(0,1));}
{ return z*z; } { return z*z - 0.5*z; } { return z*z +0.3*z.real(); } { return z*z*z; } { return z*z*exp(-z)+1.0; } z) { return z-(z*z*z-
struct TFktItParam { char* Name; FComplex f; }; const int nFktItPar=6; TFktItParam FktItPar[nFktItPar]= { { "z^2, reell ", 0}, { "z^2, komplex", CFQuadr}, { "z^2-0.5z", CFQuadr2}, { "z^3", CFKubik}, { "z*z*exp(-z)+1", CFxxexp}, { "z-(z*z*z-1)/(3*z*z+0.5*Complex(0,1)",CFNewtKubik }, }; FComplex ItFkt=0; void __fastcall TForm1::ListBox4Click(TObject *Sender) { int i=ListBox4->ItemIndex; if (i<0) i=0; ItFkt = FktItPar[i].f; } void __fastcall TForm1::ZeichneClick(TObject *Sender) { Form1->Caption="Fraktal: x0="+FloatToStr(x0)+" y0="+FloatToStr(y0)+ " x1="+FloatToStr(x1)+" y1="+FloatToStr(y1); HPALETTE hpal; if (usePalette) // nur bei 256 Farben { HPALETTE CreateMyPalette(); // Prototyp, Definition unten HPALETTE hpal=CreateMyPalette(); SelectPalette(Image1->Canvas->Handle,hpal,true); RealizePalette(Image1->Canvas->Handle); } if (ItFkt==0) ZeichneFraktal(Image1); else ZeichneFraktalComplex(Image1,ItFkt); if (usePalette) DeleteObject(hpal); // Form1->Image1->Picture->SaveToFile("c:\\Fraktal.bmp"); } // 6. b) TColor NewtonColor(int col,Complex z0, int n) { // nur für eine Bildschirmeinstellung mit mehr als 256 Farben col=col%256; for (int i=0; i
266
12 Lösungen
if (col < 64) { col=col*4; // if (i%3==0) return RGB(256-col,0,0); else if (i%3==1) return RGB(0,256-col,0); else return RGB(0,0,256-col); } else return RGB(0,256-col,256-col); // türkis } } // falls keine Lösung paßt: return RGB(256-col,256-col,256-col); // grau } int GradEinheitsPolynom; void ZeichneNewtonFraktale(TImage* Image, FComplex f, FComplex abl) { const int max_it = 200; // 50,100,200 aborted = false; // für b) for (int px=0; (px <= Image->Width-1) && (!aborted); px++) { for (int py=0;(py<=Image->Height-1) && (!aborted); py++) { Complex z0, z(x_Welt(px,x0,x1,Image->Width), y_Welt(py,y1,y0,Image->Height)); int i = 0; do { z0 = z; z = z0 - f(z0)/abl(z0); i++; } while ((i <= max_it) && (norm(z-z0)>1E-10)); TColor color; if (GradEinheitsPolynom==0) color=IndexColor(i); else color=NewtonColor(i,z0,GradEinheitsPolynom); if (i > max_it) color = clBlack; Image->Canvas->Pixels[px][py] = color; } // Damit ein eventuelles Anklicken des Buttons "Abbruch" erkannt wird // und die Pixel gezeichnet werden. if (!usePalette && px%5==0) Application->ProcessMessages(); } // Form1->Image1->Picture->SaveToFile("c:\\Newton.bmp"); } Complex fu2(const Complex& z) { return z*z-1.0; } Complex fu2_abl(const Complex& z) { return 2.0*z; } Complex fu3(const Complex& z) { return z*z*z-1.0; } Complex fu3_abl(const Complex& z) { return 3.0*z*z; } Complex fu4(const Complex& z) { return z*z*z*z-1.0; } Complex fu4_abl(const Complex& z) { return 4.0*z*z*z; } Complex fu5(const Complex& z) { return z*z*z*z*z-1.0; } Complex fu5_abl(const Complex& z) { return 5.0*z*z*z*z; } Complex fu6(const Complex& z) { return z*z*z*z*z*z-1.0; } Complex fu6_abl(const Complex& z) { return 6.0*z*z*z*z*z; } Complex f1(const Complex& z) Complex f1_abl(const Complex& z)
{ return z*z+1.0; } { return 2.0*z; }
Complex f3(const Complex& z) Complex f3_abl(const Complex& z)
{ return z*z*z-z; } { return 3.0*z*z-1.0; }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
267
Complex f4(const Complex& z) Complex f4_abl(const Complex& z)
{ return exp(z)-z; } { return exp(z)-1.0; }
Complex f5(const Complex& z) Complex f5_abl(const Complex& z)
{ return z*z*z-z*z+z+1.0; } { return 3.0*z*z-2.0*z+1.0; }
struct TNewtonParam { char* Name; double x0; double y0; double x1; double y1; FComplex f; FComplex abl; int GradEinheitsPolynom; }; const int nNewtPar=10; TNewtonParam NPar[nNewtPar]= { { "z^2-1", -10, -10, 10, { "z^3-1", -10, -10, 10, { "z^4-1", -10, -10, 10, { "z^5-1", -10, -10, 10, { "z^6-1", -10, -10, 10, { { { { { };
10, 10, 10, 10, 10,
fu2, fu3, fu4, fu5, fu6,
fu2_abl, fu3_abl, fu4_abl, fu5_abl, fu6_abl,
2}, 3}, 4}, 5}, 6},
"z^2+1", -10, -10, 10, 10, f1, f1_abl, 0}, "z^3-z", -10, -10, 10, 10, f3, f3_abl, 0}, "sin", -10, -10, 10, 10, sin, cos, 0}, "e^z-z", -10, -10, 10, 10, f4, f4_abl, 0}, "z^3-z^2+z+1", -10, -10, 10, 10, f5, f5_abl,0},
FComplex fNewt=f1, fNewt_abl=f1_abl; void __fastcall TForm1::ListBox3Click(TObject *Sender) { int i=ListBox3->ItemIndex; if (i<0) i=0; x0 = NPar[i].x0; y0 = NPar[i].y0; // links oben x1 = NPar[i].x1; y1 = NPar[i].y1; // rechts unten fNewt=NPar[i].f; fNewt_abl=NPar[i].abl; GradEinheitsPolynom=NPar[i].GradEinheitsPolynom; } void __fastcall TForm1::NewtonClick(TObject *Sender) { Form1->Caption="Newton-Fraktal: x0="+FloatToStr(x0)+" y0="+FloatToStr(y0)+ " x1="+FloatToStr(x1)+" y1="+FloatToStr(y1); ZeichneNewtonFraktale(Image1, fNewt, fNewt_abl); } void __fastcall TForm1::FormCreate(TObject *Sender) { int n=GetDeviceCaps(Image1->Canvas->Handle,NUMCOLORS); moreThan256Colors = (n==-1); // -1, falls Farbtiefe größer als 8 Bits (256 Farben) if (moreThan256Colors) usePalette=false; // CountColors->Caption=IntToStr(n); for (int i=0; i< nMBAW; i++) ListBox1->Items->Add(MBPar[i].Name); for (int i=0; i< nJAW; i++) ListBox2->Items->Add(JPar[i].Name); for (int i=0; i< nNewtPar; i++) ListBox3->Items->Add(NPar[i].Name); for (int i=0; i< nFktItPar; i++) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
268
12 Lösungen
ListBox4->Items->Add(FktItPar[i].Name); }
12.5.3 Aufgabe 6.5
// Datei: d:\Loesungen\kap-6\6.5\LogStilU.cpp
#include "LogStilU.h" //--------- Aufgabe 1 -----------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Memo1->Lines->Text= "\nAufgabe 1 \n" "Die Namen der Funktionen \n" " \n" " Quersumme, ZeigeQuersumme \n" " Fibonacci, zeige_Fibonacci \n" " prim, zeige_Primzahlen, zeige_Goldbach \n" " zeige_PythagZahlentripel \n" " f3nplus1, zeige_3nplus1 \n" " \n" "beschreiben die Aufgaben dieser Funktionen im jeweiligen Zusammenhang recht " "präzise. \n"; } //--------- Aufgabe 2 -----------------------------------------------double Mehrwertsteuer(double p2,double p3) {}; double druckeAdressaufkleber(double p2){}; double doitbabe(double i, int *p1, int p2, float p3) { int y=0; if (p2==1) for (int j=0;j<=i;j++) y=y+i; else if (i==2) y=Mehrwertsteuer(p2,5); else if (i==3) y=druckeAdressaufkleber(p2); return y; }; void __fastcall TForm1::Button2Click(TObject *Sender) { int p1=17; double p3=18; double z=doitbabe(19, &p1, 20, p3); Memo1->Lines->Text= "\nAufgabe 2 \n" "Die Funktion 'doitbabe' führt in den verschiedenen if-Zweigen völlig verschiedene " "Aufgaben durch, die durch keinen Namen zusammengefasst werden können. " "\n" "Ein Aufruf dieser Funktion wird besser durch einen Aufruf der Funktionen " "Mehrwertsteuer, druckeAdressaufkleber sowie einer Funktion Summe (für den " "ersten Zweig) ersetzt. ";
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
269
} //--------- Aufgabe 3 -----------------------------------------------int ggt(int x, int y) { // x=x0, y=y0, ggT(x,y) = ggT(x0,y0) if (x<0) x=-x; if (y<0) y=-y; // ggT(x,y) = ggT(x0,y0) while (y != 0) // Ablaufprot. für den Schleifenkörper {// x y r // ggT(x,y)=ggT(x0,y0) x0 y0 int r = x%y; // x = y; // y0 y = r; // x0%y0 // ggT(x,y) = ggT(y0,x0%y0) = ggT(x0,y0) } // ggT(x,y) = ggT(x0,y0) und y=0 return x; // ggT(x,y)==x; }
x0%y0
double horner(double x, int n, double* p) { double s = 0; int i = 1; while (i <= n+1) { s = s*x + p[n-i+1]; i++; } return s; } void __fastcall TForm1::Button3Click(TObject *Sender) { Memo1->Lines->Text= "\nAufgabe 3 \n" "Für diesen Nachweis ist der Nachweis notwendig, dass die Funktion " "immer das entsprechende Ergebnis hat. Das wurde aber gerade in den " "Aufgaben von Kapitel 5.8.5 nachgewiesen. " "\n" "Da die Funktionen tatsächlich das machen, was ihr Name erwarten lässt, " "ist der entsprechende Kommentar überflüssig, da er eine reine Wiederholung " "der Anweisung davor ist. "; int a=2,b=4; double y=ggt(a,b); // y==ggt(a,b) int x=1,n=2; double p[3]={1,2,3}; double z=horner(x,n,p); // z==Horner(x,n,p) }
12.5.4 Aufgaben 6.6.4 und 6.6.6
// Datei: d:\Loesungen\kap-6\6.6\RekursU.cpp
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
270
#include "RekursU.h" //----- Aufgabe 6.6.4.1 ---------------------------------------------// Rekursive Funktionen // a) int rek_Fak(int n) { // Fakultät rekursiv berechnen if (n<=0) return 1; else return n*rek_Fak(n-1); } // b) int rek_Fib(int n) { // Fibonacci rekursiv berechnen if (n<=0) return 0; else if (n==1) return 1; else return rek_Fib(n-1) + rek_Fib(n-2); } // c) int rek_ggT(int m, int n) { // ggT rekursiv berechnen if (n==0) return m; else return rek_ggT(n, m%n); } // a) int it_Fak(int n) { int f=1; for (int i=2; i<=n; i++) f = f*i; return f; } // b) int it_Fib(int n) { // Fibonacci iterativ berechnen int fib=0,f0=0,f1=1; for (int i=0; i
12 Lösungen
12.5 Lösungen Kapitel 6
}; void __fastcall TForm1::Aufg1Click(TObject *Sender) { RichEdit1->Lines->Add("No news are good news"); for (int i=1; i<50; i++) for (int j=1; j<50; j++) if (rek_ggT(i,j)!=it_ggT(i,j)) Form1->RichEdit1->Lines->Add(IntToStr(i)+" "+IntToStr(j)); for (int i=1; i<50; i++) if (rek_Fak(i)!=it_Fak(i)) Form1->RichEdit1->Lines->Add(IntToStr(i)); for (int i=1; i<30; i++) if (rek_Fib(i)!=it_Fib(i)) Form1->RichEdit1->Lines->Add(IntToStr(i)); } //----- Aufgabe 6.6.4.2 ---------------------------------------------int ack(int n, int m) { if (n==0) return m+1; else if (m==0) return ack(n-1,1); else return ack(n-1,ack(n,m-1)); } #include <math> void __fastcall TForm1::Aufg2Click(TObject *Sender) { /* ack(0,m) = m+1 ack(1,m) = m+2 ack(2,m) = 2*m+3 ack(3,m) = 2m+3+1 */ RichEdit1->Lines->Add("No news are good news"); for (int m=0; m<50; m++) if (ack(0,m)!=m+1) RichEdit1->Lines->Add(m); for (int m=0; m<50; m++) if (ack(1,m)!=m+2) RichEdit1->Lines->Add(m); for (int m=0; m<50; m++) if (ack(2,m)!=2*m+3) RichEdit1->Lines->Add(m); // für m>10 dauert das recht lange: for (int m=0; m<10; m++) { int a=ack(3,m); int e=pow(2,m+3)-3; if (a!=e) RichEdit1->Lines->Add(IntToStr(a)+" "+e); } } //----- Aufgabe 6.6.4.3 ---------------------------------------------void __fastcall TForm1::Aufg3Click(TObject *Sender) { RichEdit1->Lines->Add("Die Lösung ist das Programm 'Rechner'"); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
271
272
12 Lösungen
} //----- Aufgabe 6.6.4.4 ---------------------------------------------AnsiString dir="c:\\test"; void __fastcall TForm1::FormCreate(TObject *Sender) { // Erzeuge das Verzeichnis, falls es nicht existiert if (CreateDirectory(dir.c_str(),0)) ShowMessage("directory '"+dir+"' created"); fnMask->Text="*.*"; fnMask->Text="*"; DriveComboBox1->Drive='C'; } void Ausgabe(AnsiString Name) { static int i=0; i++; Form1->RichEdit1->Lines->Add(IntToStr(i)+" "+Name); } void SearchSubdirs(const AnsiString Pfad,const AnsiString Mask) { WIN32_FIND_DATA FindFileData; char FileName[MAX_PATH]; strcpy(FileName,(Pfad+Mask).c_str()); HANDLE h=FindFirstFile( FileName, // Zeiger auf den Dateinamen &FindFileData); // Zeiger auf struct if (h!=INVALID_HANDLE_VALUE) { int found=1; while (found) { if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0) { // Unterverzeichnis if (strcmp(FindFileData.cFileName,".")==0) ; // "." ignorieren else if (strcmp(FindFileData.cFileName,"..")==0) ; // ".." ignorieren else // Verzeichnis, rekursiv durchsuchen { Ausgabe(Pfad+FindFileData.cFileName); SearchSubdirs(Pfad+FindFileData.cFileName+"\\",Mask); } } else Ausgabe(Pfad+FindFileData.cFileName); found=FindNextFile(h,&FindFileData); } FindClose(h); } }; void __fastcall TForm1::RecurseDirClick(TObject *Sender) { AnsiString Pfad=AnsiString(DriveComboBox1->Drive)+":\\"; AnsiString Mask=fnMask->Text; RichEdit1->Enabled=false; SearchSubdirs(Pfad,Mask); RichEdit1->Enabled=true; } #include "TVFormU.h" void SearchSubdirsTV(const AnsiString Pfad,const AnsiString Mask, TTreeNode* T); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
void __fastcall TForm1::TreeViewClick(TObject *Sender) { AnsiString Pfad=AnsiString(Form1->DriveComboBox1->Drive)+":\\"; AnsiString Mask=Form1->fnMask->Text; TVForm->TreeView1->Items->Add(TVForm->TreeView1->TopItem, Pfad); // Add fügt einen neuen Baumknoten am Ende der Liste ein, // zu der TreeView1->TopItem gehört. // TreeView1->TopItem ist der oberste Baumknoten in TreeView1. SearchSubdirsTV(Pfad,Mask,TVForm->TreeView1->Items->Item[TVForm->TreeView1>Items->Count-1]); // TreeView1->Items ist die Liste der Baumknoten in TreeView1. // TreeView1->Items->Item[0] ist der erste Baumknoten in dieser Liste TVForm->ShowModal(); } //----- Aufgabe 6.6.4.5 ---------------------------------------------#include "RekFigU.h" void __fastcall TForm1::Aufg5Click(TObject *Sender) { FormRekFig->ShowModal(); } //----- Aufgabe 6.6.6.1 ---------------------------------------------typedef AnsiString DataType ; // Datentyp der Listendaten typedef AnsiString KeyType ; // Datentyp der Schlüsselwerte #include "\Loesungen\CppUtils\BinTree.cpp" tree_node* t=0; void __fastcall TForm1::TreeInsertClick(TObject *Sender) { insert_tree_node(t, Edit1->Text, Edit2->Text); } void process_tree_node(tree_node* n) { Form1->RichEdit1->Lines->Add(n->key); for (list_node* i=n->first; i!=0; i=i->next) Form1->RichEdit1->Lines->Add(" "+i->data); }; void __fastcall TForm1::ShowTreeClick(TObject *Sender) { traverse_tree(t); } //----- Aufgabe 6.6.6.2 ---------------------------------------------/* // Eigentlich wäre hier der Datentyp int für die Listendaten angemessen: typedef int DataType ; // Datentyp der Listendaten typedef AnsiString KeyType ; // Datentyp der Schlüsselwerte #include "\CppUtils\BinTree.cpp" Damit die Definitionen von Aufgabe 6.6.6.1 verwendet werden können, wird der Datentyp AnsiString verwendet. */ Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
273
274
12 Lösungen
/* bool use_map() { return Form1->MultiMap->Checked; } */ #include "\Loesungen\CppUtils\StringUt.cpp" #include tree_node *W; void Text_lesen(char* ifn) { ifstream in; in.exceptions(ios::badbit); in.open(ifn); int LineNr=0; if (in) { string z; // string und nicht AnsiString, da getline einen String // als Parameter hat vector v; while (getline(in,z)) { LineNr++; v.clear(); parseString(AnsiString(z.c_str()),v); for (vector::iterator i=v.begin(); i!=v.end(); i++) // if (use_map) mm.insert(MMType::value_type(token.c_str(),LineNr)); // else insert_tree_node(W, *i, IntToStr(LineNr).c_str()); } } in.close(); // überflüssig } #include
// notwendig für setw
ofstream out; void print_tree_node(tree_node* n) { const int Druckpos_Seitenzahl = 25; const int n_pl = 80; // 8 // Anzahl Zahlen pro Zeile im Ausdruck const int fw_pn = 6; // Feldbreite eine Zahl im Ausdruck list_node* X=n->first; out<key.c_str()<<setw(Druckpos_Seitenzahl-n->key.Length())<<' '<<':'; int niLine = 0; while (X!=0) { if (niLine == n_pl) { out<<endl; niLine = 0; out<<setw(Druckpos_Seitenzahl+1)<<' '; }; niLine++; out<<setw(fw_pn)<<(X->data).c_str(); X = X->next; }; out<<endl; }; void print_tree(tree_node* n) { // wie traverse_tree if (n!=0) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
{ print_tree(n->left); print_tree_node(n); print_tree(n->right); } } void GenerateConcordanceList(AnsiString infn,char* outfn) { Text_lesen(infn.c_str()); out.open(outfn,ios::trunc); // if (use_map()) PrintMap(); // else traverse_tree(W); print_tree(W); out.close(); } void __fastcall TForm1::KonkordanzClick(TObject *Sender) { OpenDialog1->Filter = "Textdateien|*.TXT"; OpenDialog1->InitialDir = dir; if (OpenDialog1->Execute()) { AnsiString fn=OpenDialog1->FileName; RichEdit1->Lines->Add("Ausgabedatei: "+fn+".kon"); Application->ProcessMessages(); TCursor Save_Cursor = Screen->Cursor; Screen->Cursor = crHourGlass; // Sanduhr-Cursor anzeigen Application->ProcessMessages(); GenerateConcordanceList(fn,(fn+".kon").c_str()); Screen->Cursor = Save_Cursor; // Alten Zustand wiederherstellen } }
12.5.5 Aufgabe 6.6.4.3
// Datei: d:\Loesungen\kap-6\6.6\RechnerU.cpp
#include "RechnerU.h" //-------------------------------------------------------------------void whitespace_ueberlesen(AnsiString s, int& i) { // läßt sich auf Tabs usw. erweitern while ((i <= s.Length()) && (s[i]==' ')) i++; // (s[i] <> ' ') or "fertig" } bool SubstrInStr(AnsiString Substr, AnsiString Str) { // true, falls SubStr in Str enthalten ist return Str.Pos(Substr); // SubstrInStr("a","abc"); // true // SubstrInStr("abc","a"); // false // SubstrInStr("abc","abc");// true // SubstrInStr("",""); // false } double Zahl_lesen(AnsiString s, int& i) { AnsiString ops; while ((i<=s.Length())&& SubstrInStr(s[i],"0123456789,")) ops = ops + s[i++]; // s[i] zuweisen, anschließend i++ Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
275
276
12 Lösungen
// (s[i] keine Ziffer) or (fertig) try {return StrToFloat(ops);} catch(...) { // Falls s[i] keine Ziffer war ist ops=="" ShowMessage("Unerwartetes Zeichen: '"+AnsiString(s[i])+"'"); return 0; } } char Operand_einlesen(AnsiString s, int& i) { if (i <= s.Length()) return s[i++]; return ' '; //damit kein undefinierter Wert zurückgegeben // wird und der Fehler anschl. erkannt werden kann } double MultExpr(AnsiString s, int& i); // Prototyp für die rekursiven Aufrufe double PrimExpr(AnsiString s, int& i); // Prototyp für die rekursiven Aufrufe void errormessage(int i, AnsiString msg, AnsiString s) { AnsiString m=s; m.Insert("<---",i+1); if (msg=="") ShowMessage("Syntaxfehler bei Pos. "+IntToStr(i)+"\n"+m); else ShowMessage(msg+IntToStr(i)+"\n"+m); } double AdditiveExpr(AnsiString s, int& i) { whitespace_ueberlesen(s,i); double result = MultExpr(s,i); /* 1 */ while ( (i<=s.Length()) && (SubstrInStr(s[i],"+-")) ) { char op = Operand_einlesen(s,i); whitespace_ueberlesen(s,i); switch (op) { case '+': result = result + MultExpr(s,i); // term break; case '-': result = result - MultExpr(s,i); // term break; } whitespace_ueberlesen(s,i); } return result; } double MultExpr(AnsiString s, int& i) { double result = PrimExpr(s,i); whitespace_ueberlesen(s,i); while ( (i <= s.Length()) && (SubstrInStr(s[i],"*/")) ) { char op = Operand_einlesen(s,i); whitespace_ueberlesen(s,i); switch (op) { case '*': result = result * PrimExpr(s,i); // factor break; case '/': result = result / PrimExpr(s,i); // factor break; } whitespace_ueberlesen(s,i); } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
return result; } //----- Aufgabe 6.4.3. ----------------------------------------------#include AnsiString id_lesen(AnsiString s, int& i) { // ähnlich wie die Syntax für einen identifier AnsiString result; while ((i<=s.Length())&& (isalpha(s[i]) || isdigit(s[i]) || (s[i]=='_') )) { result = result + UpCase(s[i]); // s[i] zuweisen, anschließend i++ i++; } return result; } #include <math.h> // Ende Lösung Aufgabe 3 double PrimExpr(AnsiString s, int& i) { double result; if (SubstrInStr(s[i],"1234567890,")) result=Zahl_lesen(s,i); else if (s[i]=='(') { i++; // weiterlesen nach "(" nicht vergessen whitespace_ueberlesen(s,i); result = AdditiveExpr(s,i); if (s[i]== ')') { i++; whitespace_ueberlesen(s,i); } else errormessage(i,"')' expected: ",s); } else if (s[i]=='-') { i++; // weiterlesen nach "-–" nicht vergessen whitespace_ueberlesen(s,i); result = -PrimExpr(s,i); } // Lösung von Aufgabe 3. else if (isalpha(s[i])) { AnsiString id=id_lesen(s,i); whitespace_ueberlesen(s,i); if (id=="PI") result=M_PI; else if(id=="PIQ") result=M_PI*M_PI; else if (s[i]=='(') { i++; if (id=="SIN") result = sin(AdditiveExpr(s,i)); else if (id=="COS") result = cos(AdditiveExpr(s,i)); else { result = 0; errormessage(i,"invalid function: ",s); } if (s[i]==')') i++; else errormessage(i,"')' expected: ",s); } else errormessage(i,"'(' expected: ",s); } // Ende Lösung von Aufgabe 3. Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
277
278
12 Lösungen
else errormessage(i,"",s); return result; } double expression(AnsiString t) { int i=1; return AdditiveExpr(t,i); } void __fastcall TForm1::rechneClick(TObject *Sender) { if (Edit1->Text.Trim()!="") { double result=expression(Edit1->Text); Memo1->Lines->Add(Edit1->Text+" = "+result); } } //-------------------------------------------------------------------void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key) { if (Key==13) rechneClick(Sender); } //-------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { Edit1->Text=""; Memo1->Clear(); } //--------------------------------------------------------------------
void testeZeile(AnsiString s, double erg) { double r = expression(s); if (erg != r) Form1->Memo1->Lines->Add(s+"="+FloatToStr(r)+ " nicht: "+FloatToStr(erg)); } void teste_parser1() { testeZeile("1+(2 + 3 testeZeile("1+(2 - 3 testeZeile("1+(2 * 3 testeZeile("1+(6 / 3
) ) ) )
", ", ", ",
6); 0); 7); 3);
testeZeile("1-(2 testeZeile("1-(2 testeZeile("1-(2 testeZeile("1-(6
+ * /
3 3 3 3
) ) ) )
", ", ", ",
-4); 2); -5); -1);
testeZeile("3*(2 testeZeile("3*(2 testeZeile("3*(2 testeZeile("3*(6
+ * /
3 3 3 3
) ) ) )
", ", ", ",
15); -3); 18); 6);
testeZeile("10/(2 testeZeile("10/(2 testeZeile("12/(2 testeZeile("10/(6 }
+ * /
3 3 3 3
) ) ) )
", ", ", ",
2); -10); 2); 5);
void teste_parser() Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
279
{ testeZeile(" 1+2/(1 + 1 ) ", 2); testeZeile("4 - 5 + (10 / 10)", 0); testeZeile(" 1/sin(1) ", 1.188395105778); testeZeile("sin(3)*sin(3)+cos(3)*cos(3) ",1); testeZeile(" 1+2/(1 + 1 ) ",2); testeZeile(" -(3 + 4 ) ",-7); testeZeile(" 10 -(3 + 4 ) ",3); testeZeile(" 1 ",1); testeZeile(" -1 ",-1); testeZeile(" 1,1+ 2,2",3.3); testeZeile("1,1+2,2+3,3",6.6 ); testeZeile("3*4 + 5*6 ",42); testeZeile("(4 - 5) *10 / 10",-1); testeZeile("4 -(5 + 10) / 10",2.5); testeZeile("4 - 5 + -10 / 10",-2); testeZeile("4 - 5 + (10) / 10)",0); testeZeile("4 - 5 + (10 / 10)",0); testeZeile(" sin(1) ",0.841470984807897); testeZeile(" 1/sin(1) ",1.18839510577812); testeZeile(" sin(1+sin(1)) ",0.963590724541833); testeZeile(" sin(2) ",0.909297426825682); testeZeile(" sin(1)+sin(2) ",1.75076841163358); testeZeile(" 1/(sin(1)+sin(2)) ",0.571177771631678); testeZeile(" 2+1/(sin(1)+sin(2)) ",2.57117777163168); testeZeile("sin(3)*sin(3)+cos(3)*cos(3) ",1); testeZeile(" 1+ pi ",4.14159265358979); testeZeile(" 1/(1+ pi) ",0.241453007005224); testeZeile(" piq/(pi*pi) ",1); } void __fastcall TForm1::TestClick(TObject *Sender) { teste_parser(); teste_parser1(); }
12.5.6 Aufgabe 6.6.4.4
// Datei: d:\Loesungen\kap-6\6.6\TVFormU.cpp
#include "TVFormU.h" TTVForm *TVForm; __fastcall TTVForm::TTVForm(TComponent* Owner) : TForm(Owner) { } void SearchSubdirsTV(const AnsiString Pfad,const AnsiString Mask, TTreeNode* T) { WIN32_FIND_DATA FindFileData; char FileName[MAX_PATH] ; strcpy(FileName,(Pfad+Mask).c_str()); HANDLE h=FindFirstFile(FileName, // Zeiger auf den Dateinamen &FindFileData); // Zeiger auf struct if (h!=INVALID_HANDLE_VALUE) { int found=1; while (found) { Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
280
12 Lösungen
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0) { // Unterverzeichnis if (strcmp(FindFileData.cFileName,".")==0) ; // "." ignorieren else if (strcmp(FindFileData.cFileName,"..")==0) ; // ".." ignorieren else // Verzeichnis, rekursiv durchsuchen { TVForm->TreeView1->Items->AddChild(T,FindFileData.cFileName); // AddChild fügt TreeView1 einen neuen Knoten am Ende der Liste // hinzu, auf die T zeigt int n=T->Count; // Anzahl der Knoten in der Liste, auf die T zeigt SearchSubdirsTV(Pfad+FindFileData.cFileName+"\\",Mask,T->Item[n1]); // T->Item[n-1] ist der letzte Knoten in der Liste, auf die T zeigt } } //else Ausgabe(Pfad+FindFileData.cFileName); found=FindNextFile(h,&FindFileData); } FindClose(h); T=T->Parent; } }; /* void __fastcall TForm1::SortClick(TObject *Sender) { TreeView1->SortType=stBoth; TreeView1->AlphaSort(); } */ // #include "RekursU.h" void __fastcall TTVForm::FormCreate(TObject *Sender) { TreeView1->Align=alClient; }
12.5.7 Aufgabe 6.6.4.5
// Datei: d:\Loesungen\kap-6\6.6\RekFigU.cpp
#include "RekFigU.h" TFormRekFig *FormRekFig; __fastcall TFormRekFig::TFormRekFig(TComponent* Owner) : TForm(Owner) { } //----------Koch'sche Kurven ------------------------------------int round(double x) { return int(x+0.5); } #include void Koch(TCanvas* C, int n, double leftx, double lefty, double rightx, double righty) { const double r = std::sqrt(3)/6;//ca. r=0.29; if (n<=1) { C->MoveTo(round(leftx), round(lefty)); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
C->LineTo(round(rightx), round(righty)); } else { Koch(C, n-1, leftx, lefty, (rightx + 2.0*leftx)/3.0,(righty + 2.0*lefty)/3.0); Koch(C,n-1, (rightx + 2.0*leftx)/3.0,(righty + 2.0*lefty)/3.0, 0.5*(rightx+leftx) - r*(lefty-righty), 0.5*(righty + lefty) + r*(leftx-rightx)); Koch(C,n-1, 0.5*(rightx + leftx) - r*(lefty-righty), 0.5*(righty + lefty) + r*(leftx-rightx), (2.0*rightx + leftx)/3.0,(2.0*righty + lefty)/3.0); Koch(C,n-1, (2.0*rightx + leftx)/3.0,(2.0*righty + lefty)/3.0, rightx, righty); } } void ClearImages(int W, int H) { FormRekFig->Image1->Canvas->Brush->Color=clWhite;// FillRect(TRect(0,H,W,H)); FormRekFig->Image2->Canvas->Brush->Color=clWhite;// FillRect(TRect(0,H,W,H)); FormRekFig->Image3->Canvas->Brush->Color=clWhite;// FillRect(TRect(0,H,W,H)); FormRekFig->Image4->Canvas->Brush->Color=clWhite;// FillRect(TRect(0,H,W,H)); FormRekFig->Image1->Canvas->FillRect(TRect(0,0,W,H)); FormRekFig->Image2->Canvas->FillRect(TRect(0,0,W,H)); FormRekFig->Image3->Canvas->FillRect(TRect(0,0,W,H)); FormRekFig->Image4->Canvas->FillRect(TRect(0,0,W,H)); } int level=1; void __fastcall TFormRekFig::Koch_Click(TObject *Sender) { FormRekFig->Caption = "Koch'sche Kurven, n=2, 3, 4, 5"; int W=Image1->ClientWidth-1; int H=Image1->ClientHeight-3; ClearImages(W, H+2); Koch(FormRekFig->Image1->Canvas,2,0,H,W,H); Koch(FormRekFig->Image2->Canvas,3,0,H,W,H); Koch(FormRekFig->Image3->Canvas,4,0,H,W,H); Koch(FormRekFig->Image4->Canvas,5,0,H,W,H); } //----- Aufgabe 6.6.4.5 ---------------------------------------------void Dreieck(TCanvas* C, int n, double x, double y, double L) { const double r = std::sqrt(3); if (n<=1) { // zeichnet ein gleichseitiges Dreieck im Punkt (x,y) // mit der Seitenlänge L TPoint Eckpunkte[4]; Eckpunkte[0]=Point(x,y); Eckpunkte[1]=Point(x+L,y); Eckpunkte[2]=Point(x+L/2,y-L*r/2); Eckpunkte[3]=Point(x,y); C->Brush->Color = clBlack; C->Polygon(Eckpunkte, 3); } else { Dreieck(C, n-1, x, y, L/2); Dreieck(C, n-1, x+L/2, y, L/2); Dreieck(C, n-1, x+L/4, y-L*r/4, L/2); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
281
282
} } void __fastcall TFormRekFig::rekDreiecke_Click(TObject *Sender) { FormRekFig->Caption = "Rekursive Dreiecke , n=1, 2, 5, 9"; int W=Image1->ClientWidth-1; int H=Image1->ClientHeight-3; ClearImages(W,H+2); Dreieck(FormRekFig->Image1->Canvas,1,0,H,H); Dreieck(FormRekFig->Image2->Canvas,2,0,H,H); Dreieck(FormRekFig->Image3->Canvas,5,0,H,H); Dreieck(FormRekFig->Image4->Canvas,9,0,H,H); }
12.5.8 Aufgabe 6.6.6.3
// Datei: d:\Loesungen\kap-6\6.6\DupFilesU.cpp
#include "DupFilesU.h" //----- Aufgabe 6.6.6.3 ---------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { DriveComboBox1->Drive='C'; RichEdit1->ScrollBars=ssVertical; Form1->Caption="Duplicate Files"; Form1->MultiMap->Checked=true; OneDrive->Caption="Ein Laufwerk"; AllDrives->Caption="Alle Laufwerke"; } typedef AnsiString DataType ; // Datentyp der Listendaten typedef AnsiString KeyType ; // Datentyp der Schlüsselwerte #include "\Loesungen\CppUtils\BinTree.cpp" tree_node* W=0; void process_tree_node(tree_node* n) { if (n->first->next!=0) { // nur Listen mit mehr als einem Knoten ausgeben Form1->RichEdit1->Lines->Add(n->key.Trim()); for (list_node* i=n->first; i!=0; i=i->next) Form1->RichEdit1->Lines->Add(" "+i->data); } }; #include <map> using namespace std; typedef multimap MMType; MMType mm; void PrintMap() { for (MMType::iterator i=mm.begin(); i!=mm.end(); ) { Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.5 Lösungen Kapitel 6
MMType::iterator k, first=mm.lower_bound((*i).first); MMType::iterator j, last=mm.upper_bound((*i).first); k=first; k++; if (k!=last) // nur doppelte Einträge anzeigen { Form1->RichEdit1->Lines->Add((*i).first.Trim()); for (j=first; j!=last; j++) { Form1->RichEdit1->Lines->Add(" "+(*j).second); i++; // Mit jedem gefundenen Wert hochzählen }; } else i++; } } bool use_map() { return Form1->MultiMap->Checked; } AnsiString AddLeadingBlanks(AnsiString s, int n) { // Ergänzt den String s um so viele führende Nullen, daß // der Funktionswert die Länge n hat. Mit dieser Funktion // wird erreicht, daß ein Vergleich der Strings "12" < " 2" // dasselbe Ergebnis hat wie der Vergleich der Zahlen 12<2. // Ohne führendes Leerzeichen wäre das Ergebnis verschieden. return s.StringOfChar(' ',n-s.Length())+s; } void SearchSubdirs(AnsiString Pfad) { WIN32_FIND_DATA FindFileData; char FileName[MAX_PATH] ; AnsiString Mask="*.*"; strcpy(FileName,(Pfad+Mask).c_str()); HANDLE h=FindFirstFile( FileName, // Zeiger auf den Dateinamen &FindFileData); // Zeiger auf struct if (h!=INVALID_HANDLE_VALUE) { int found=1; while (found) { if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0) { // Unterverzeichnis if (strcmp(FindFileData.cFileName,".")==0) ; // "." ignorieren else if (strcmp(FindFileData.cFileName,"..")==0) ; // ".." ignorieren else // Verzeichnis, rekursiv durchsuchen SearchSubdirs(Pfad+FindFileData.cFileName+"\\"); } else { AnsiString Low=IntToStr(FindFileData.nFileSizeLow); AnsiString High=IntToStr(FindFileData.nFileSizeHigh); if (High=="0") High=" "; KeyType Key=AddLeadingBlanks(High,10)+AddLeadingBlanks(Low,10)+" "+FindFileData.cFileName; DataType Data=Pfad+FindFileData.cFileName; if (use_map()) mm.insert(MMType::value_type(Key,Data)); else insert_tree_node(W, Key, Data); } found=FindNextFile(h,&FindFileData); } FindClose(h); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
283
284
12 Lösungen
} }; void __fastcall TForm1::OneDriveClick(TObject *Sender) { // Durchsucht nur das ausgewählte Laufwerk TCursor Save_Cursor = Screen->Cursor; Screen->Cursor = crHourGlass; // Cursor als Sanduhr RichEdit1->Clear(); AnsiString Laufwerk=AnsiString(DriveComboBox1->Drive)+":\\"; SearchSubdirs(Laufwerk); if (use_map()) PrintMap(); else traverse_tree(W); Screen->Cursor = Save_Cursor; } //-------------------------------------------------------------------void __fastcall TForm1::AllDrivesClick(TObject *Sender) { // Durchsucht alle Laufwerke TCursor Save_Cursor = Screen->Cursor; Screen->Cursor = crHourGlass; // Cursor als Sanduhr RichEdit1->Clear(); for (int i=0; iItems->Count; i++) { AnsiString Laufwerk=AnsiString(DriveComboBox1->Items->Strings[i][1])+":\\"; SearchSubdirs(Laufwerk); } if (use_map()) PrintMap(); else traverse_tree(W); Screen->Cursor = Save_Cursor; } //--------------------------------------------------------------------
12.5.9 Aufgabe 6.6.6.4
// Datei: d:\Loesungen\kap-6\6.6\DirTreeU.cpp
#include #include "DirTreeU.h" #pragma link "cdiroutl" //----- Aufgabe 6.6.6.4 ---------------------------------------------// a) struct File_list_node; // Vorwärtsreferenz, da // dir_node einen Zeiger auf File_list_node und // umgekehrt struct dir_node { AnsiString DirName; double FilesSize; File_list_node* first; File_list_node* last; };
// für Teil d)
struct File_list_node { AnsiString FileName; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
dir_node* sub_dir; File_list_node* next; }; void insert_File(AnsiString dn, AnsiString fn, dir_node* dir) { // fügt einen Knoten in die Liste ein File_list_node* tmp=new File_list_node; // (1) tmp->FileName = fn; tmp->next = 0; tmp->sub_dir = 0; // kein Unterverzeichnis if (dir->last==0) // erster Knoten dir->first = tmp; else // zweiter oder weiterer Knoten dir->last->next = tmp; dir->last = tmp; } void insert_Dir_node(AnsiString DirName, dir_node*& dir) { if (dir==0) { dir = new dir_node; dir->first=0; dir->last=0; dir->DirName=DirName; dir->FilesSize=0; } else ShowMessage("dir!=0: "+DirName); } // b) double // Teil d), sonst void SearchSubdirs(AnsiString Pfad, dir_node*& dir) { insert_Dir_node(Pfad,dir); // <-- Änderung WIN32_FIND_DATA FindFileData; char FileName[MAX_PATH] ; AnsiString Mask="*.*"; strcpy(FileName,(Pfad+Mask).c_str()); HANDLE h=FindFirstFile( FileName, // Zeiger auf den Dateinamen &FindFileData); // Zeiger auf struct if (h!=INVALID_HANDLE_VALUE) { int found=1; while (found) { if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0) { // Unterverzeichnis if (strcmp(FindFileData.cFileName,".")==0) ; // "." ignorieren else if (strcmp(FindFileData.cFileName,"..")==0) ; // ".." ignorieren else // Verzeichnis, rekursiv durchsuchen { insert_File(Pfad,FindFileData.cFileName, dir); // <-- Änderung dir->FilesSize += // Teil d) SearchSubdirs(Pfad+FindFileData.cFileName+"\\", dir->last->sub_dir); // <-- Änderung } } else insert_File(Pfad,FindFileData.cFileName,dir); // <-- Änderung LARGE_INTEGER c; // Teil d) c.u.LowPart=FindFileData.nFileSizeLow; // Teil d) c.u.HighPart=FindFileData.nFileSizeHigh; // Teil d) dir->FilesSize+=c.QuadPart; // Teil d) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
285
286
12 Lösungen
found=FindNextFile(h,&FindFileData); } FindClose(h); return dir->FilesSize;
// Teil d)
} return -1; }; /* nur zum Testen void ShowDir(dir_node* Dirs) { //Form1->Memo1->Lines->Add(Dirs->DirName+FloatToStr(Dirs->FilesSize)); for (File_list_node* i=Dirs->first; i!=0; i=i->next) { // Form1->Memo1->Lines->Add(i->FileName); if (i->sub_dir!=0) ShowDir(i->sub_dir); } } */ // c) void ShowDirTree(dir_node* Dirs, TTreeNode* T) { for (File_list_node* i=Dirs->first; i!=0; i=i->next) if (i->sub_dir!=0) { Form1->TreeView1->Items->AddChild(T, i->FileName+": "+ FloatToStrF(i>sub_dir->FilesSize,ffNumber,18,0)); ShowDirTree(i->sub_dir,T->Item[T->Count-1]); } } void __fastcall TForm1::FormCreate(TObject *Sender) { Form1->Caption="Show directory tree and size "; TreeView1->Align=alLeft; } //-------------------------------------------------------------------void __fastcall TForm1::ShowDirClick(TObject *Sender) { // GetLogicalDriveStrings(BufSize,lpBuffer); // ermöglicht es, die Laufwerke zu lesen dir_node* Dirs=0; AnsiString Laufwerk=AnsiString(DriveComboBox1->Drive)+":\\"; SearchSubdirs(Laufwerk,Dirs); if (Dirs!=0) { TreeView1->Items->Add(TreeView1->TopItem, Dirs->DirName+": "+ FloatToStrF(Dirs->FilesSize,ffNumber,18,0)); ShowDirTree(Dirs, TreeView1->Items->Item[TreeView1->Items->Count-1]); } } void __fastcall TForm1::SortClick(TObject *Sender) { TreeView1->SortType=stBoth; TreeView1->AlphaSort(); }
12.5.10 Aufgabe 6.8
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6 // Datei: d:\Loesungen\kap-6\6.8\OvrLoadU.cpp
#include "OvrLoadU.h" //----- Aufgabe 1 -----------------------------------------------int Abs(int x) { if (x<0) return -x; else return x; } double Abs(double x) { if (x<0) return -x; else return x; } long double Abs(long double x) { if (x<0) return -x; else return x; } void __fastcall TForm1::Abs_Click(TObject *Sender) { short b1; Abs(b1); // Abs(int) unsigned b2; //Abs(b2); // Ambiguity between 'Abs(int)' and 'Abs(long double)' enum {e1= -1, e2=1} b3; Abs(b3); // Abs(int); char b6; Abs(b6); // Could not find a match for Abs(char*) } //----- Aufgabe 2 -----------------------------------------------typedef double(*real_func)(double); #include void Newton_Nullstelle_mit_Abl(double x0, real_func f, real_func Abl, int& i, double& x) { const double eps=std::numeric_limits<double>::epsilon()*100; // i zählt die Iterationen - zur Vermeidung von Endlosschleifen i = 0; x = x0; do { x0 =x; // Startwert für die nächste Iteration i++; x = x0 - f(x0)/Abl(x0); } while ((Abs(x-x0) >= eps) && (i <= 100)); } typedef long double(*ld_real_func)(long double); void Newton_Nullstelle_mit_Abl(long double x0, ld_real_func f, ld_real_func Abl, int& i, long double& x) { const long double eps=std::numeric_limits::epsilon()*100; // i zählt die Iterationen - zur Vermeidung von Endlosschleifen i = 0; x = x0; do { x0 =x; // Startwert für die nächste Iteration Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
287
288
12 Lösungen
i++; x = x0 - f(x0)/Abl(x0); } while ((Abs(x-x0) >= eps) && (i <= 100)); } int r; // damit r an die Quadratfunktion übergeben werden // kann, ohne die Parameterliste zu ändern - nicht schön double quadrat(double x) { return x*x - r; } double quadrat_abl(double x) { return 2*x; } long double quadrat_ld(long double x) { return x*x - r; } long double quadrat_abl_ld(long double x) { return 2*x; } void __fastcall TForm1::NewtonClick(TObject *Sender) { for (r=1; r<=100; r++) { int i; long double xd; Newton_Nullstelle_mit_Abl(r, quadrat_ld,quadrat_abl_ld, i, xd); Form1->Memo1->Lines->Add(Format("LD r=%g it=%d newt=%g sqrt=%g d=%g", OPENARRAY(TVarRec,(1.0*r,i,xd,sqrt(r),Abs(xd-sqrt(r)))))); double x; Newton_Nullstelle_mit_Abl(r, quadrat,quadrat_abl, i, x); Form1->Memo1->Lines->Add(Format("r=%g it=%d newt=%g sqrt=%g d=%g", OPENARRAY(TVarRec,(1.0*r,i,x,sqrt(r),Abs(x-sqrt(r)))))); }; } //----- Aufgabe 3 -----------------------------------------------void f(bool b) { } void f(int i) { } void __fastcall TForm1::BoolClick(TObject *Sender) { f(true); // f(bool) f(TRUE); // f(int) } //----- Aufgabe 4 -----------------------------------------------void g(char *) { } void g(const char *) { } void h(char *, double d=0) { } void h(const char *, int i=0) { }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
void __fastcall TForm1::FktAufrufeClick(TObject *Sender) { g("abc"); // g(char*) const char* s1="abc"; char* s2="abc"; char* const s3="abc"; g(s1); // g(const char*) g(s2); // g(char*) g(s3); // g(char*) const char s4[20]="abc"; char s5[20]="abc"; char const s6[20]="abc"; g(s4); // g(const char*) g(s5); // g(char*) g(s6); // g(const char*) // // //
h(s1,1); // h(const char*,int) h(s2,1); // Ambiguity between h(char*,double) and h(const char*,int) h(s3,1); // Ambiguity between h(char*,double) and h(const char*,int) h(s4,1); // h(const char*,int) h(s5,1); // Ambiguity between h(char*,double) and h(const char*,int) h(s6,1); // h(const char*,int)
}
12.5.11 Aufgaben 6.9
// Datei: d:\Loesungen\kap-6\6.9\GlOpFktU.cpp
#include "GlOpFktU.h" //----- Aufgabe 1 -----------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Memo1->Lines->Text= "Der Operator ^ kann nur für selbstdefinierte Datentypen überladen " "werden. Da '-' eine höhere Priorität als '^' hat, wird x^n-1 als " "x^(n-1) ausgewertet. Üblicherweise ist das aber (x^n)-1"; } //----- Aufgabe 2 -----------------------------------------------struct CBruch { int z,n; // z: Zähler, n: Nenner }; // a) ==, != inline bool operator==(const CBruch& q1, const CBruch& q2) { return q1.z*q2.n==q2.z*q1.n; } // Die folgenden Anweisungen wurden mit leichten Änderungen aus utility.h // übernommen. Die Definitionen aus utility.h gehören zum C++-Standard: inline bool operator!=(const CBruch& x, const CBruch& y) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
289
290
12 Lösungen
{ return !(x == y); } // b) <, <=, >, >= inline bool operator<(const CBruch& q1, const CBruch& q2) { return q1.z*q2.n < q2.z*q1.n; } // Die folgenden Anweisungen wurden mit leichten Änderungen aus utility.h // übernommen. Die Definitionen aus utility.h gehören zum C++-Standard: inline bool operator>(const CBruch& x, const CBruch& y) { return y < x; } inline bool operator<=(const CBruch& x, const CBruch& y) { return !(y < x); } inline bool operator>=(const CBruch& x, const CBruch& y) { return !(x < y); } // c) +, -, *, /, skalare Multiplikation mit kürzen (-) == *(-1) int ggT(int a, int b) { // siehe Aufgabe 5.8.5.3 int x=a; int y=b; while (y != 0) { int r = x%y; x = y; y = r; } return x; // ggT(a,b)==x; } CBruch kuerze_Bruch(CBruch q) { int t = ggT(q.z,q.n); if (t==0) return q; CBruch result = {q.z/t,q.n/t}; return result; }; inline CBruch operator+(const CBruch& p, const CBruch& q) { CBruch result={p.z*q.n + q.z*p.n , p.n*q.n}; return kuerze_Bruch(result); }; inline CBruch operator-(const CBruch& p, const CBruch& q) { CBruch result={p.z*q.n - q.z*p.n , p.n*q.n}; return kuerze_Bruch(result); }; inline CBruch operator*(const CBruch& p, const CBruch& q) { CBruch result={p.z*q.z,p.n*q.n}; return kuerze_Bruch(result); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
}; inline CBruch operator/(const CBruch& p, const CBruch& q) { CBruch result={p.z*q.n,p.n*q.z}; return kuerze_Bruch(result); }; AnsiString BruchToAnsiString(CBruch q) { return IntToStr(q.z)+'/'+IntToStr(q.n); }; // d) Zum Initialisieren: CBruch InitCBruch(int z,int n=1) { // InitBruch(x): x/1 CBruch b={z,n}; return b; } CBruch Test_geom_Reihe1(CBruch p,int n) { // summiere die Potenzen: 1 + q + q^2 ... + q^n CBruch s=InitCBruch(1), q=InitCBruch(1); for (int i=1; i<=n; i++) { q=q*p; s=s+q; } return s; } CBruch Test_geom_Reihe2(CBruch p,int n) { // Summenformel: (p^(n+1) - 1)/(p-1) CBruch q=p, Eins=InitCBruch(1); for (int i=2; i<=n+1; i++) q=q*p; return (q-Eins)/(p-Eins); } #include <math.h> // für pow void Test_geom_Reihe(int max_z, int max_n) { for (int z=-max_z; z<max_z; z++) for (int n=-max_n; n<max_n; n++) if (z!=n && n!=0) { int N=abs(n); CBruch p=InitCBruch(z,n); // z/n CBruch g1=Test_geom_Reihe1(p,N); CBruch g2=Test_geom_Reihe2(p,N); // x: setze z/n in die Summenformel ein CBruch x=kuerze_Bruch(InitCBruch(pow(z,N+1)-pow(n,N+1),pow(n,N)*(z-n))); if (g1!=g2 || g1!=x) // no news are good news Form1->Memo1->Lines->Add("p="+BruchToAnsiString(p)+ " g1="+BruchToAnsiString(g1)+ " g2="+BruchToAnsiString(g2)+ " x="+BruchToAnsiString(x)); } } void __fastcall TForm1::BruchClick(TObject *Sender) { Test_geom_Reihe(7,7); // größere Werte: Bereichsüberlauf }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
291
292
12 Lösungen
//----- Aufgabe 3 -----------------------------------------------#include using namespace std; ostream& operator<<(ostream& f, const CBruch& b) { return f<>(istream& f, CBruch& b) { char Bruchstrich; f>>b.z>>Bruchstrich>>b.n; return f; } #include <string> #include <sstream> string BruchToStr(CBruch b) { ostringstream o; o<>b; return b; } void __fastcall TForm1::Bruch_IO_OpClick(TObject *Sender) { CBruch b1={1,2}; Memo1->Lines->Add(BruchToStr(b1).c_str()); string s="3/4"; CBruch b2=StrToBruch(s); Memo1->Lines->Add(BruchToStr(b2).c_str()); }
12.6 Lösungen Kapitel 7
12.6.1 Aufgaben 7.1
// Datei: d:\Loesungen\kap-7\7.1\Unit0.cpp
// Diese include-Anweisung für Aufgabe b) ausklammern: // #include "Unit0.h" double x0;
// // double x1=1; // // const double c=3; // //
a) b) a) b) a) b)
Compiler Fehler: Bezeichner 'x0' mehrfach deklariert Linker Warnung: Public symbol defined in both ... Compiler Fehler: Bezeichner 'x1' mehrfach deklariert Keine Meldung des Linkers Compiler Fehler: Bezeichner 'c' mehrfach deklariert Keine Meldung des Linkers
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.6 Lösungen Kapitel 7
extern const double d=3;// // extern const int e=3; // // void f(double) // { // x0++; };
293
a) b) a) b) a) b)
Compiler Fehler: Bezeichner 'd' mehrfach deklariert Keine Meldung des Linkers Compiler Fehler: Bezeichner 'd' mehrfach deklariert Keine Meldung des Linkers Keine Meldung des Compilers Linker-Fehler: Unresolved external 'f(int)'
// Datei: d:\Loesungen\kap-7\7.1\Unit1.cpp
#include "Unit1.h" //------------ Aufgaben 7.1 -----------------------------------------//------------ Aufgabe 1 --------------------------------------------// Die Datei Unit0.cpp wurde mit "Projekt|Dem Projekt hinzufügen" // dem Projekt hinzugefügt. // Damit die Aufgaben 2 und 3 kompiliert werden können, muss // das Makro Aufgabe1 auf den Wert 0 gesetzt werden: // #define Aufgabe1 1 #define Aufgabe1 0 #if Aufgabe1 #include "Unit0.h" #endif void call_f() { #if Aufgabe1 f(4); // Damit die Funktion aus Unit0.cpp aufgerufen wird. #endif } //------------ Aufgabe 2 --------------------------------------------#include "Unit2.h" // manuell eingefügt #include "Unit3.h" // manuell eingefügt void __fastcall TForm1::Button1Click(TObject *Sender) { call_f(); MischeKarten(Spielkarten); if (AnzahlVerteilteKarten+AnzahlKartenProHandCaption="n="+IntToStr(AnzahlVerteilteKarten); zeigeHand(Memo1,Hand[0]); } //------------ Aufgabe 3 --------------------------------------------#include "\Loesungen\CppLib\GraphUtils.h"; #include "\Loesungen\CppLib\TimeUtils.h"; // b) // Die Dateien \Loesungen\CppLib\GraphUtils.cpp und // \Loesungen\CppLib\TimeUtils.cpp werden in das Projekt aufgenommen // c) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
294
12 Lösungen
// Entweder werden die folgenden obj-Dateien in das Projekt aufgenommen // oder mit #pragma link eingebunden: // #pragma link "\\Loesungen\\CppLib\\GraphUtils.obj" // #pragma link "\\Loesungen\\CppLib\\TimeUtils.obj" // d) // Die Bibliothek "Mylib.lib" wird durch // tlib MyLib +GraphUtils.obj+TimeUtils.obj // angelegt und in das Projekt aufgenommen (tlib aus dem Verzeichnis // 'bin' des C++Builders). #include <math> // für sin void __fastcall TForm1::Button2Click(TObject *Sender) { double Start1=HRTimeInSec(); PlotFunction(-10, 10, sin, Image1); double End1=HRTimeInSec(); Form1->Memo1->Lines->Add("t1="+FloatToStr(End1-Start1)); }
12.6.2 Aufgabe 7.1.2
// Datei: d:\Loesungen\kap-7\7.1\Unit3.h
#ifndef Unit3H #define Unit3H struct TSpielkarte { char* Farbe; int Wert; }; const int AnzahlKartenProHand=5; typedef TSpielkarte THand[AnzahlKartenProHand]; const int AnzahlSpielkarten=16; extern TSpielkarte Spielkarten[AnzahlSpielkarten]; extern int AnzahlVerteilteKarten; extern int AnzahlMitspieler; const int MaxAnzahlMitspieler=100; extern THand Hand[MaxAnzahlMitspieler]; void MischeKarten(TSpielkarte*); #include // für TMemo void zeigeHand(TMemo* Memo, THand H); inline void verteileKarten(TSpielkarte* K, THand H) { for (int i=0; i
12.6 Lösungen Kapitel 7
// Datei: d:\Loesungen\kap-7\7.1\Unit3.cpp
#include "Unit3.h" TSpielkarte Spielkarten[AnzahlSpielkarten] = { {"Pik",9},{"Pik",10}, {"Pik",11}, {"Pik",12}, {"Herz",9},{"Herz",10}, {"Herz",11}, {"Herz",12}, {"Kreuz",9},{"Kreuz",10}, {"Kreuz",11}, {"Kreuz",12}, {"Karo",9},{"Karo",10}, {"Karo",11}, {"Karo",12} }; // zur Vereinfachung nur wenig Karten int AnzahlVerteilteKarten=0; int AnzahlMitspieler=2; THand Hand[MaxAnzahlMitspieler]; #include // für swap #include <stdlib> // für rand() void MischeKarten(TSpielkarte* K) { for (int i=0; i<50;i++) { int p=rand()%AnzahlSpielkarten; int q=rand()%AnzahlSpielkarten; std::swap(K[p],K[q]); } } void zeigeHand(TMemo* Memo, THand H) { Memo->Clear(); for (int i=0; iLines->Add(AnsiString(H[i].Farbe)+" "+H[i].Wert); }
// Datei: d:\Loesungen\kap-7\7.1\Unit2.cpp
#include "Unit2.h" #include "Unit3.h" // manuell eingefügt TForm2 *Form2; __fastcall TForm2::TForm2(TComponent* Owner) : TForm(Owner) { Show(); } void __fastcall TForm2::Button1Click(TObject *Sender) { MischeKarten(Spielkarten); if (AnzahlVerteilteKarten+AnzahlKartenProHandCaption="n="+IntToStr(AnzahlVerteilteKarten); zeigeHand(Memo1,Hand[1]); }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
295
296
12 Lösungen
12.6.3 Aufgabe 7.1.3
// Datei: d:\Loesungen\CppLib\GraphUtils.h
#ifndef GraphUtilsH #define GraphUtilsH double x_Welt(int px, double x0, double x1, double W); // transformiert px aus [0,W] in Welt-Koordinaten [x0,x1] double y_Welt(int py, double y0, double y1, double H); // transformiert py aus [0,H] in Welt-Koordinaten [y1,y0] int x_Bildschirm(double x, double x0, double x1, double W); // transformiert x aus [x0,x1] in Bildkoordinaten [0,W] int y_Bildschirm(double y, double y0, double y1, double H); // transformiert y aus [y0,y1] in Bildkoordinaten [H,0] void Gitternetz(TImage* Image1, double x0,double x1,double dx, int W, double y0,double y1,double dy, int H); // void __fastcall TForm1::ClearImageClick(TObject *Sender) void ClearImage(TImage* Image1); typedef double(*real_func)(double) ; void PlotFunction(double x0, double x1, real_func f, TImage* Image1); #endif
// Datei: d:\Loesungen\CppLib\GraphUtils.cpp
#include "GraphUtils.h" double x_Welt(int px, double x0, double x1, double W) { // transformiert px aus [0,W] in Welt-Koordinaten [x0,x1] return x0 + px*(x1-x0)/W; } double y_Welt(int py, double y0, double y1, double H) { // transformiert py aus [0,H] in Welt-Koordinaten [y1,y0] return y1 + py*(y0-y1)/H; } int x_Bildschirm(double x, double x0, double x1, double W) { // transformiert x aus [x0,x1] in Bildkoordinaten [0,W] return (x-x0)*W/(x1-x0); } int y_Bildschirm(double y, double y0, double y1, double H) { // transformiert y aus [y0,y1] in Bildkoordinaten [H,0] return (y-y1)*H/(y0-y1); } #include <math.h> #include
// für fabs // für TImage
void Gitternetz(TImage* Image1, double x0,double x1,double dx, int W, double y0,double y1,double dy, int H) { bool xAchse_sichtbar=false; bool yAchse_sichtbar=false; int xt=x_Bildschirm(0,x0,x1,W); if (0<=xt && xt<= W) xAchse_sichtbar=true; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.6 Lösungen Kapitel 7
else xt=0; int yt=y_Bildschirm(0,y0,y1,H); if (0<=yt && yt<=H) yAchse_sichtbar=true; else yt=0; int x=0, i=0; Image1->Canvas->Pen->Color = clGray; while (x<W) { // zeichne Parallelen zur y-Achse Image1->Canvas->MoveTo(x,H); Image1->Canvas->LineTo(x,0); Image1->Canvas->TextOut(x,yt,FloatToStrF(x0+i*dx,ffGeneral,4,4)); i++; x = x_Bildschirm(x0+i*dx,x0,x1,W); } int y=0; i=0; while (yCanvas->MoveTo(0,y); Image1->Canvas->LineTo(W,y); if (fabs(y1-i*dy) > 0.001) // sonst Schönheitsfehler Image1->Canvas->TextOut(xt,y,FloatToStrF(y1-i*dy,ffGeneral,4,4)); i++; y = y_Bildschirm(y1-i*dy,y0,y1,H); } // Koordinatenkreuz schwarz nachzeichnen: Image1->Canvas->Pen->Color = clBlack; if (xAchse_sichtbar) { Image1->Canvas->MoveTo(xt,H); Image1->Canvas->LineTo(xt,0); } if (yAchse_sichtbar) { Image1->Canvas->MoveTo(0,yt); Image1->Canvas->LineTo(W,yt); } } void ClearImage(TImage* Image1) { TColor PenAlt = Image1->Canvas->Pen->Color; Image1->Canvas->Brush->Color=clWhite; Image1->Canvas->Pen->Color=clWhite; Image1->Canvas->Rectangle(0,0,Image1->ClientWidth,Image1->ClientHeight); Image1->Canvas->Pen->Color=PenAlt; } typedef double(*real_func)(double) ; void PlotFunction(double x0, double x1, real_func f, TImage* Image1) { // a) das alte Bild löschen: Image1->Canvas->Brush->Color = clWhite; Image1->Canvas->Rectangle(0,0,Image1->Width,Image1->Height); // b) bestimme den minimalen und maximalen Wert von f(x) im Bereich [x0,x1]: double y0=f(x_Welt(0, x0, x1, Image1->Width)); // y0=min(f(x)) double y1=y0; // y1=max(f(x)) for (int px=1; px<=Image1->Width-/*–*/1; px++) { // transformiere px in Welt-Koordinaten double x=x_Welt(px, x0, x1, Image1->Width); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
297
298
double y=f(x); if (yy1) y1=y; }; // zeichne die Kurve: Image1->Canvas->Pen->Color = clRed; for (int px=0; px<=Image1->Width-/*–*/1; px++) { // transformiere px in Welt-Koordinaten double x=x_Welt(px, x0, x1, Image1->Width); // transformiere y in Bildkoordinaten int py=y_Bildschirm(f(x), y0, y1, Image1->Height); if (px == 0) Image1->Canvas->MoveTo(px,py); else Image1->Canvas->LineTo(px,py); } // c) zeichne das Gitternetz: double dx=(x1-x0)/10; // 10 Gitterlinien double dy=(y1-y0)/10; // 10 Gitterlinien Gitternetz(Image1, x0,x1,dx, Image1->ClientWidth, y0,y1,dy, Image1->ClientHeight); }
// Datei: d:\Loesungen\CppLib\TimeUtils.h
#ifndef TimeUtilsH #define TimeUtilsH // double GetHRFrequency(); // extern double HRFrequency; double HRTimeInSec(); // Diese Routinen können wie folgt verwendet werden: /* double Start1=HRTimeInSec(); s=Sum1(n); double End1=HRTimeInSec(); Form1->Memo1->Lines->Add("t1="+FloatToStr(End1-Start1)); */ #endif
// Datei: d:\Loesungen\CppLib\TimeUtils.cpp
#include "TimeUtils.h" double GetHRFrequency() { LARGE_INTEGER f; BOOL bf=QueryPerformanceFrequency(&f); if (bf) return f.QuadPart; else return -1; } double HRFrequency=GetHRFrequency();; double HRTimeInSec() { LARGE_INTEGER c; BOOL bc=QueryPerformanceCounter(&c); if (bc) return c.QuadPart/HRFrequency; else return -1; }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.6 Lösungen Kapitel 7
299
// Diese Routinen können wie folgt verwendet werden: /* double Start1=HRTimeInSec(); s=Sum1(n); double End1=HRTimeInSec(); Form1->Memo1->Lines->Add("t1="+FloatToStr(End1-Start1)); */
12.6.4 Aufgabe 7.2
// Datei: d:\Loesungen\kap-7\7.2\UseDLLExplU.cpp
#include "UseDLLExplU.h" HINSTANCE hTimeDLL, hGraphDLL; void __fastcall TForm1::FormCreate(TObject *Sender) { hTimeDLL = LoadLibrary("TimeDLL.dll"); if (hTimeDLL==0) ShowMessage("'TimeDLL' nicht geladen"); hGraphDLL = LoadLibrary("GraphDLL.dll"); if (hGraphDLL==0) ShowMessage("'GraphDLL' nicht geladen"); } #include <math> // für sin extern double HRFrequency; void __fastcall TForm1::Button1Click(TObject *Sender) { typedef int __stdcall TMin(int X, int Y); typedef double(*real_func)(double) ; /* Das Ergebnis "timedll.def" von "impdef timedll timedll.dll": LIBRARY
TIMEDLL.DLL
EXPORTS @HRTimeInSec$qv ___CPPdebugHook
@1 @2
; HRTimeInSec() ; ___CPPdebugHook
Das Ergebnis "graphdll.def" von "impdef graphdll graphdll.dll": LIBRARY
GRAPHDLL.DLL
EXPORTS @ClearImage$qp15Extctrls@TImage @6 ; ClearImage(Extctrls::TImage *) @Gitternetz$qp15Extctrls@TImagedddidddi @5 ; Gitternetz(Extctrls::TImage *, double, double, double, int, double, double, double, int) @PlotFunction$qddpqd$dp15Extctrls@TImage @7 ; PlotFunction(double, double, double (*)(double), Extctrls::TImage *) @x_Bildschirm$qdddd @3 ; x_Bildschirm(double, double, double, double) @x_Welt$qiddd @1 ; x_Welt(int, double, double, double)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
300
@y_Bildschirm$qdddd double) @y_Welt$qiddd ___CPPdebugHook
12 Lösungen
@4
; y_Bildschirm(double, double, double,
@2 @8
; y_Welt(int, double, double, double) ; ___CPPdebugHook
*/ typedef double __stdcall THRTimeInSec(); THRTimeInSec* HRTimeInSec=(THRTimeInSec*) GetProcAddress(hTimeDLL,"@HRTimeInSec$qv"); if (HRTimeInSec== 0) ShowMessage("'HRTimeInSec' nicht gefunden"); double Start1=HRTimeInSec(); typedef void __stdcall TPlotFunction(double x0, double x1, real_func f, TImage* Image1); TPlotFunction* PlotFunction=(TPlotFunction*) GetProcAddress(hGraphDLL,"@PlotFunction$qddpqd$dp15Extctrls@TImage"); if (PlotFunction== 0) ShowMessage("'PlotFunction' nicht gefunden"); PlotFunction(-10, 10, sin, Image1); double End1=HRTimeInSec(); Memo1->Lines->Add("t1="+FloatToStr(End1-Start1)); }
12.6.5 Aufgabe 7.2
// Datei: d:\Loesungen\kap-7\7.2\UseDLLImplU.cpp
#include "UseDLLImplU.h" //-------------------------------------------------------------------// Die Importdatei wurde dem Projekt hinzugefügt typedef double(*real_func)(double) ; __declspec(dllimport) void PlotFunction (double x0, double x1, real_func f, TImage* Image1); __declspec(dllimport) double HRTimeInSec(); extern double HRFrequency; #include <math> // für sin void __fastcall TForm1::Button1Click(TObject *Sender) { double Start1=HRTimeInSec(); PlotFunction(-10, 10, sin, Image1); double End1=HRTimeInSec(); Form1->Memo1->Lines->Add("t1="+FloatToStr(End1-Start1)); }
12.6.6 Aufgabe 7.3
// Datei: d:\Loesungen\kap-7\7.3\NameSpU.cpp Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.6 Lösungen Kapitel 7
301
#include "NameSpU.h" //----------- Aufgabe 7.3.1. ----------------------------------------void f(){}; void g(){}; namespace A { void f(){}; void g(){}; } namespace B { using ::f; // a) global f using A::g; // b) A’s g } void __fastcall TForm1::Aufg1Click(TObject *Sender) { using namespace B; f(); // c) calls ::f //g(); // d) calls A::g } //----------- Aufgabe 7.3.2. ----------------------------------------#include <math> #include "GraphUtils.h" #include "TimeUtils.h" void __fastcall TForm1::Aufg2Click(TObject *Sender) { using namespace TimeUtils; using namespace GraphUtils; double Start1=HRTimeInSec(); PlotFunction(-10, 10, sin, Form1->Image1); double End1=HRTimeInSec(); Form1->Memo1->Lines->Add("t1="+FloatToStr(End1-Start1)); } //----------- Aufgabe 7.3.3. ----------------------------------------// Man kann die beiden Dateien einfach in einen eigenen Namensbereich aufnehmen: /* N1.h: #ifndef N1H #define N1H namespace N1 { #include "f1.h" } #endif N1.cpp: #include "N1.h" namespace N1 { #include "f1.cpp" } Analog für N2. */ #include "N1.h"
// außerdem N1.cpp dem Projekt hinzufügen
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
302
#include "N2.h"
12 Lösungen
// außerdem N2.cpp dem Projekt hinzufügen
void __fastcall TForm1::Aufg3Click(TObject *Sender) { int i1=N1::N::f(1); int i2=N2::N::f(1); }
//--------------------------------------------------------------------
// Datei: d:\Loesungen\kap-7\7.3\f1.cpp
#include "f1.h" namespace N{ int f(const int i) { return i; } } // end of namespace
// Datei: d:\Loesungen\kap-7\7.3\f2.cpp
#include "f2.h" namespace N{ int f(int i) { return i+1; } } // end of namespace
// Datei: d:\Loesungen\kap-7\7.3\N1.cpp
#include "N1.h" namespace N1 { #include "f1.cpp" }
// Datei: d:\Loesungen\kap-7\7.3\N2.cpp
#include "N2.h" namespace N2 { #include "f2.cpp" }
12.7 Lösungen Kapitel 8
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
12.7.1 Aufgabe 8.1.5 und 8.1.7
// Datei: d:\Loesungen\kap-8\8.1\KlassenU.cpp
#include "KlassenU.h" // --- Aufgabe 1 ----------------------------------------------------/* Ein Konstruktor hat die Aufgabe, alle Datenelemente einer Klasse so zu initialisieren, dass sie beim Aufruf einer beliebigen Elementfunktion in einem definierten Zustand sind. In einem Destruktor sollen alle Ressourcen wieder freigegeben werden, die in einem Konstruktor reserviert wurden. */ class C { int n, max; int* a; public: C() { max=100; a=new int[max]; } // Fehler: n wird nicht initialisiert, aber in show_data verwendet C(int i) { n=1; a=new int[100]; a[0]=i; }; // Fehler: max wird nicht initialisiert, aber in add_data verwendet C(int i,int j) { n=2; max=100; a=new int[100]; a[1]=i; a[2]=j; }; // Fehler: max und a[0] werden nicht initialisiert void add_data(int d) { if (n<max-1) { n++; a[n]=d; } } void show_data() { for (int i=0; iMemo1->Lines->Add(IntToStr(a[i])); } // Der Destruktor mit delete[] a fehlt }; void __fastcall TForm1::Aufg1Click(TObject *Sender) { // }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
303
304
12 Lösungen
// --- Aufgabe 2 ----------------------------------------------------// a) class C1 { int x,y,z; public: C1(int x_=0, int y_=0, int z_=0) { x=x_; y=y_; z=z_; } }; // Da die Elemente nur initialisiert und keine Ressourcen reserviert // werden, ist kein Destruktor notwendig // b) class C2 { int* x; public: C2(int n) { x=new int[n]; } }; // Destruktor mit delete[] x ist notwendig // c) #include using namespace std; class C3 { ifstream f; public: C3(char* FileName) { f.open(FileName); } }; // Da für die Klasse C3 kein expliziter Konstruktor definiert wird, // erzeugt der Compiler einen. Dieser ruft die Destruktoren aller // Elemente von C3 auf, also auch den von f. Da ifstream eine Klasse // der Standardbibliothek von C++ ist und alle Klassen dieser Bibliothek // Destruktoren haben, die sich anständig verhalten und alle ihre // Ressourcen wieder freigeben, ist für C3 kein Destruktor notwendig. // Ein Destruktor mit close ist aber auch kein Fehler. void __fastcall TForm1::Aufg2Click(TObject *Sender) { // } // --- Aufgabe 3 ----------------------------------------------------const double pi=3.14159265358979323846; // a) class CKreis{ double r; public: CKreis(double Radius) { r=Radius; } double Radius() { return r; } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
void setzeRadius(double Radius) { r=Radius; } double Flaeche() { return r*r*pi; } double Umfang() { return 2*r*pi; } AnsiString toStr() { return "Kreis mit Radius "+FloatToStr(r); } }; // b) class CQuadrat{ // Elementfunktionen außerhalb definieren double a; public: CQuadrat(double Seitenlaenge):a(Seitenlaenge){}; double Seitenlaenge(); void setze_Seitenlaengen(double Seitenlaenge); double Flaeche(); double Umfang(); AnsiString toStr(); }; double CQuadrat::Seitenlaenge() { return a; } void CQuadrat::setze_Seitenlaengen(double Seitenlaenge) { a=Seitenlaenge; } double CQuadrat::Flaeche() { return a*a; } double CQuadrat::Umfang() { return 4*a; } AnsiString CQuadrat::toStr() { return "Quadrat mit Seitenlänge "+FloatToStr(a); } // c) class CRechteck{ double a,b; // Seitenlängen public: CRechteck(double a_, double b_) { a=a_; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
305
306
b=b_; }; double Seitenlaenge_a() { return a; } double Seitenlaenge_b() { return b; } void setze_Seitenlaengen(double a_,double b_) { a=a_; b=b_; } double Flaeche() { return a*b; } double Umfang() { return 2*(a+b); } AnsiString toStr() { return "Rechteck mit a="+FloatToStr(a)+" b="+FloatToStr(b); } }; // e) void test_Figuren() { CKreis k1(10); Form1->Memo1->Lines->Add(k1.toStr()); Form1->Memo1->Lines->Add(k1.Umfang()); Form1->Memo1->Lines->Add(k1.Flaeche()); CKreis* pk=new CKreis(10); Form1->Memo1->Lines->Add(pk->toStr()); Form1->Memo1->Lines->Add(pk->Umfang()); Form1->Memo1->Lines->Add(pk->Flaeche()); CQuadrat q(10); Form1->Memo1->Lines->Add(q.toStr()); Form1->Memo1->Lines->Add(q.Umfang()); Form1->Memo1->Lines->Add(q.Flaeche()); CQuadrat* pq=new CQuadrat(10); Form1->Memo1->Lines->Add(pq->toStr()); Form1->Memo1->Lines->Add(pq->Umfang()); Form1->Memo1->Lines->Add(pq->Flaeche()); CRechteck r(10,20); Form1->Memo1->Lines->Add(r.toStr()); Form1->Memo1->Lines->Add(r.Umfang()); Form1->Memo1->Lines->Add(r.Flaeche()); CRechteck* pr=new CRechteck(7.5,12.5); Form1->Memo1->Lines->Add(pr->toStr()); Form1->Memo1->Lines->Add(pr->Umfang()); Form1->Memo1->Lines->Add(pr->Flaeche()); } void __fastcall TForm1::Aufg3Click(TObject *Sender) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
{ test_Figuren(); } // --- Aufgabe 4 ----------------------------------------------------class CGrundstueck { char* Anschrift; double Kaufpreis; double Flaeche; public: CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_) { Anschrift = new char[strlen(Anschrift_)+1]; strcpy(Anschrift,Anschrift_); Kaufpreis=Kaufpreis_; Flaeche=Flaeche_; } ~CGrundstueck() { delete[] Anschrift; } AnsiString toStr() const // const: konstante Elementfunktion, siehe { // Abschnitt 8.2.10 "Konstante Klassenelemente und Objekte" return "Grundstück in "+AnsiString(Anschrift)+ " KP: "+ FloatToStr(Kaufpreis)+ " DM Flaeche: "+FloatToStr(Flaeche); } }; void display(const CGrundstueck& g) { Form1->Memo1->Lines->Add(g.toStr()); } /* In den Klassen CGrundstueck usw. muss im Konstruktor der Speicherplatz für die Anschrift mit new reserviert und das Argument Anschrift_ in diesen Speicherbereich kopiert werden, da die Anschrift ein Zeiger auf einen nullterminierten String ist. Wegen new ist außerdem ein Destruktor notwendig. Würde man als Datentyp für die Anschrift eine Stringklasse (z.B. string oder AnsiString) wählen, wäre diese Klasse einfacher, da auch kein Destruktor notwendig ist: class CGrundstueck { string Anschrift; double Kaufpreis; double Flaeche; public: CGrundstueck(AnsiString Anschrift_, double Kaufpreis_, double Flaeche_) { Anschrift = Anschrift; Kaufpreis=Kaufpreis_; Flaeche=Flaeche_; } string toStr() const // const: konstante Elementfunktion, siehe { // Abschnitt 8.2.10 "Konstante Klassenelemente und Objekte" return "Grundstück in "+Anschrift+ " KP: "+FloatToStr(Kaufpreis)+ " DM Flaeche: "+FloatToStr(Flaeche); } }; Deshalb sollte man Stringklassen immer dem Datentyp char* vorziehen. Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
307
308
12 Lösungen
Es liegt nahe, die gemeinsamen Datenelemente 'Anschrift' und 'Kaufpreis' der verschiedenen Klassen wiederzuverwenden. Diese Möglichkeit wird im Kapitel über Vererbung behandelt. Die folgenden Klassen sind ähnlich aufgebaut wie die Klasse CGrundstueck und zeigen, welche Ersparnis an Arbeits- und Testaufwand die Stringklassen anstelle von nullterminierten Strings hätten. */ class CEigentumswohnung { char* Anschrift; double Kaufpreis; double Wohnflaeche; int AnzahlZimmer; public: CEigentumswohnung(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, int AnzahlZimmer_) { Anschrift = new char[strlen(Anschrift_)+1]; strcpy(Anschrift,Anschrift_); Kaufpreis=Kaufpreis_; Wohnflaeche=Wohnflaeche_; AnzahlZimmer=AnzahlZimmer_; } ~CEigentumswohnung() { delete[] Anschrift; } AnsiString toStr() const { return "ETW in "+AnsiString(Anschrift)+ " KP: "+FloatToStr(Kaufpreis)+ " DM Wohnfläche: "+FloatToStr(Wohnflaeche)+" Zimmer: "+IntToStr(AnzahlZimmer); } }; void display(const CEigentumswohnung& e) { Form1->Memo1->Lines->Add(e.toStr()); } class CEinfamilienhaus { char* Anschrift; double Kaufpreis; double Wohnflaeche; double Grundstuecksgroesse; int AnzahlZimmer; public: CEinfamilienhaus(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, double Grundstuecksgroesse_, int AnzahlZimmer_) { Anschrift = new char[strlen(Anschrift_)+1]; strcpy(Anschrift,Anschrift_); Kaufpreis=Kaufpreis_; Wohnflaeche=Wohnflaeche_; Grundstuecksgroesse=Grundstuecksgroesse_; AnzahlZimmer=AnzahlZimmer_; } ~CEinfamilienhaus() { delete[] Anschrift; } AnsiString toStr() const Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
309
{ return "EFH in "+AnsiString(Anschrift)+ " KP: "+FloatToStr(Kaufpreis)+ " DM Wohnfläche: "+FloatToStr(Wohnflaeche)+" Anzahl Zimmer: "+ IntToStr(AnzahlZimmer)+" Grundstück: "+FloatToStr(Grundstuecksgroesse); } }; void display(const CEinfamilienhaus& e) { Form1->Memo1->Lines->Add(e.toStr()); } class CSchloss { char* Anschrift; double Kaufpreis; int AnzahlSchlossgeister; public: CSchloss(char* Anschrift_, double Kaufpreis_, int AnzahlSchlossgeister_) { Anschrift = new char[strlen(Anschrift_)+1]; strcpy(Anschrift,Anschrift_); Kaufpreis=Kaufpreis_; AnzahlSchlossgeister=AnzahlSchlossgeister_; } ~CSchloss() { delete[] Anschrift; } AnsiString toStr() const { return "Schloss in "+AnsiString(Anschrift)+ " KP: "+FloatToStr(Kaufpreis) +" DM Schlossgeister: "+IntToStr(AnzahlSchlossgeister); } }; void display(const CSchloss& g) { Form1->Memo1->Lines->Add(g.toStr()); } class CGewerbeobjekt { char* Anschrift; double Kaufpreis; double Nutzflaeche; char* Nutzungsart; // z. B. Büro, Fabrik, Restaurant public: CGewerbeobjekt(char* Anschrift_, double Kaufpreis_, double Nutzflaeche_, char* Nutzungsart_) { Anschrift = new char[strlen(Anschrift_)+1]; strcpy(Anschrift,Anschrift_); Kaufpreis=Kaufpreis_; Nutzflaeche=Nutzflaeche_; Nutzungsart=new char[strlen(Nutzungsart_)+1]; strcpy(Nutzungsart,Nutzungsart_); } ~CGewerbeobjekt() { delete[] Anschrift; delete[] Nutzungsart; } AnsiString toStr() const { Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
310
12 Lösungen
AnsiString s="Gewerbeobjekt in "+AnsiString(Anschrift)+ " KP: "+ FloatToStr(Kaufpreis)+" DM Nutzfläche: "+ FloatToStr(Nutzflaeche)+" Nutzungsart: "+Nutzungsart; return s; } }; void display(const CGewerbeobjekt& g) { Form1->Memo1->Lines->Add(g.toStr()); } void test_Immo() { // CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_) CGrundstueck g("Tübingen",100000,400); display(g); // CEigentumswohnung(char* Anschrift_, double Kaufpreis_, // double Wohnflaeche_, int AnzahlZimmer_) CEigentumswohnung etw("Stuttgart",500000,120,5); display(etw); // CEinfamilienhaus(char* Anschrift_, double Kaufpreis_, // double Wohnflaeche_, double Grundstuecksgroesse_, int AnzahlZimmer_) CEinfamilienhaus efh("Kiel",300000,150,900,5); display(efh); CSchloss s("Neuschwanstein",30000000,5); display(s); // CGewerbeobjekt(char* Anschrift_, double Kaufpreis_, // double Nutzflaeche_, char* Nutzungsart_) CGewerbeobjekt gew("München",400000,200,"Lagerhalle"); display(gew); } void __fastcall TForm1::Aufg4Click(TObject *Sender) { test_Immo(); } // --- Aufgabe 5 ----------------------------------------------------namespace N_Aufgabe5 { void display(AnsiString s, int i=-1) { if (i>=0) s=s+IntToStr(i); Form1->Memo1->Lines->Add(s); } class C{ int x; public: C (int x_=0) { // Beim Aufruf ohne Argument ein Standardx=x_; // konstruktor display(" Konstruktor: ", x); } ~C () { display(" Destruktor: ",x); } }; void f1(C c) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
{ display(" };
in f1(): Werteparameter");
void f2(const C& c) { display(" in f2(): Referenzparameter"); }; C f3(int i) { display(" in f3(): return-Wert"); return C(i); }; void test1() { C x(1); C* z=new C(2); display("vor x=C(3)"); x=C(3); display("vor f1(4)"); f1(4); display("vor f2(x)"); f2(x); display("vor f3(5)"); x=f3(5); delete z; display("Ende von test()"); } /* Die Anweisungen der Funktion test1 erzeugen die eingerückten Meldungen: C x(1); Konstruktor: 1 C* z=new C(2); Konstruktor: 2 display("vor x=C(3)"); vor x=C(3) x=C(3); Konstruktor: 3 Destruktor: 3 display("vor f1(4)"); vor f1(4) f1(4); Konstruktor: 4 in f1(): Werteparameter Destruktor: 4 display("vor f2(x)"); vor f2(x) f2(x); in f2(): Referenzparameter display("vor f3(5)"); vor f3(5) x=f3(5); in f3(): return-Wert Konstruktor: 5 Destruktor: 5 delete z; Destruktor: 2 display("Ende von test()"); Ende von test() } Destruktor: 5 */ Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
311
312
12 Lösungen
void test2() { display("vor p1"); C* p1=new C[2]; delete[] p1; display("vor p2"); C* p2=new C[2]; delete p2; display("vor p3"); C* p3=new C; delete[] p3; display("vor p4"); C* p4=new C(4); delete[] p4; display("Ende von test()"); } /* b) Die Funktion test2 erzeugt unter anderem die folgende Ausgabe: vor p1 Konstruktor: 0 Konstruktor: 0 Destruktor: 0 Destruktor: 0 vor p2 Konstruktor: 0 Konstruktor: 0 Destruktor: 0 Die Anweisungen nach display(„vor p3") sind falsch, da ein mit new[] reservierter Speicherbereich mit delete (und nicht mit delete[]) wieder freigegeben wird, bzw. ein mit new reservierter Speicherbereich mit delete[] (und nicht mit delete). Gibt man bei dem Ausdruck nach new runde Klammern an, sind die Werte in den Klammern Argumente für den Konstruktor und nicht etwa Arraygrenzen. Deswegen muss nach diesem Aufruf delete und nicht delete[] aufgerufen werden. */ } // end of namespace N_Aufgabe5 void __fastcall TForm1::Aufg5Click(TObject *Sender) { N_Aufgabe5::test1(); N_Aufgabe5::test2(); } // --- Aufgabe 6 ----------------------------------------------------class MeinString { char* s; // Zeiger auf nullterminierten String int n; // Länge des Strings public: MeinString(const char* p) // 1 { // p muss auf einen nullterminierten String zeigen n=strlen(p); s=new char[n+1]; strcpy(s,p); }; MeinString(char c) // 2 { // kann auch mit einem int-Argument aufgerufen werden n=1; s=new char[n+1]; *s=c; *(s+1)='\0'; }; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
const char* c_str() { // 'strcpy(s1.c_str(),"abc")' geht ohne "const", aber nicht mit. // Da ein solcher Aufruf die Klasseninvariante zerstören kann, // ist hier const notwendig. return s; } void replace(int from, char* x); // für Aufgabe 8.1.7.2 }; void test_6() { MeinString s("abc"); Form1->Memo1->Lines->Add(IntToStr(strlen(s.c_str()))); } void __fastcall TForm1::Aufg6Click(TObject *Sender) { test_6(); } // --- Aufgabe 7 ----------------------------------------------------namespace N_Aufgabe_7 { class C { typedef int T; T x; public: C(T x_) { x=x_; } T& data() // T data() wäre zu langsam { return x; } }; void test() { C c(1); c.data()=17; // Da der Funktionswert von data ein Referenztyp ist, kann man mit dieser // public Funktion den Wert des private Elements x verändern und damit die // Einschränkung des Zugriffsrechts außer Kraft setzen. // Diesen vermutlich nicht beabsichtigten Effekt kann man verhindern, // indem man den Rückgabetyp der Funktion data als konstanten // Referenzparameter definiert: // Mit einer Elementfunktion, die eine Referenz zurückgibt, können auch // Datenelemente von Objekten angesprochen werden, die nicht mehr // existieren. } } // end of namespace N_Aufgabe_7 void __fastcall TForm1::Aufg7Click(TObject *Sender) { N_Aufgabe_7::test(); } // --- Aufgabe 8 ----------------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
313
314
class CHRTimer { LARGE_INTEGER HRFrequency, start, end; bool HRTimerSupported; public: CHRTimer() { HRTimerSupported= QueryPerformanceFrequency(&HRFrequency); QueryPerformanceCounter(&start); } void Start() { QueryPerformanceCounter(&start); } void End() { QueryPerformanceCounter(&end); } AnsiString TimeStr() { if (HRTimerSupported) // QuadPart hat den Datentyp int64 return FloatToStr((double(end.QuadPart)-start.QuadPart)/ HRFrequency.QuadPart); else return "No High Resolution Timer supported"; } }; struct Ct { int a[1000]; Ct(int i_) { a[0]=i_;}; }; int ft(const Ct& x) { return x.a[0]; } void time_test(int n) { CHRTimer t1,t2; t1.Start(); Ct c(1); for (int i=0; iMemo1->Lines->Add(t1.TimeStr()); t2.Start(); for (int i=0; iMemo1->Lines->Add(t2.TimeStr()); } void __fastcall TForm1::Aufg8Click(TObject *Sender) { time_test(1000); time_test(10000); time_test(100000); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
315
} // --- Aufgabe 9 ----------------------------------------------------// //
Mit diesem Trick lassen sich alle Vorteile des Zugriffsrechts private zunichte machen.
//
#undef private ist hier unpassend.
// // //
Wenn eine Klasse aber nicht alle Elementfunktionen zur Verfügung stellt, die ein Anwender benötigt (also nicht vollständig ist), kann dieser Trick aber doch eine gewisse Berechtigung haben.
// --- Aufgabe 10 ---------------------------------------------------class MeinStack { // ein Stack mit einem dynamisch reservierten Array typedef int T; // damit der Datentyp leicht geändert werden kann int SPtr; // Index des "obersten" Elements T* Array; // Zeiger auf das erste Element int Max; // Anzahl der reservierten Arrayelemente public: MeinStack(int s):SPtr(-1),Max(s) { Array=new T[s];} ~MeinStack() { delete [] Array; } void push(T a) { Array[++SPtr]=a; } T pop() { return Array[SPtr--]; } bool full() { return SPtr>=Max; } bool empty() { return SPtr<0; } }; void test_stack(int n) { Form1->Memo1->Lines->Add("test_stack n="+IntToStr(n)); MeinStack s(n); for (int i=0; iMemo1->Lines->Add(s.pop()); } void __fastcall TForm1::Aufg9Click(TObject *Sender) { for (int i=0; i<10; i++) test_stack(i); } // --- Aufgabe 11 ---------------------------------------------------class MeineListe { typedef int T;
// T: der Datentyp der "Nutzdaten"
struct list_node { // Knoten einer verketteten Liste. T data; // Kann auch lokal in der Klasse list_node* next; // definiert werden. }; list_node *first,*last; public: MeineListe():first(0),last(0) {}; void insert(T data) { /* Erzeugt einen neuen Listen-Knoten und fügt diesen nach last ein. Last zeigt anschließend auf das letzte und first auf das erste Element der Liste. */ list_node* tmp=new list_node; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
316
tmp->data = data; tmp->next = 0; if (last == 0) first = tmp; else last->next = tmp; last = tmp; }
12 Lösungen
// // // //
2. 3.2 von insert 1. 3.
void show() { for (list_node* tmp=first; tmp != 0; tmp = tmp->next) Form1->Memo1->Lines->Add(tmp->data); } ~MeineListe() { // verwendet die private Hilfsfunktion von unten while (first!=0) erase(first); last=0; } void test_MeineListe(int n) { Form1->Memo1->Lines->Add("test_MeineListe n="+IntToStr(n)); MeineListe L; for (int i=0; inext; delete tmp; }; }; void __fastcall TForm1::Aufg10Click(TObject *Sender) { MeineListe list; for (int i=0; i<10; i++) list.test_MeineListe(i); } // --- Aufgabe 8.1.7 ------------------------------------------------AnsiString s2b1, s2b2; void MeinString::replace(int from, char* x) { // ersetze die Zeichen ab 'from' durch die von x int xn=strlen(x); // Vorbedingung für strlen: x zeigt auf einen // nullterminierten String for (int i=0; from+i
12.7 Lösungen Kapitel 8
void __fastcall TForm1::Aufg_817_12Click(TObject *Sender) { AnsiString s1a= "Bei den geometrischen Figuren müssen die Seitenlängen bzw. der " "Radius >= 0 sein."; AnsiString s1b= "Damit die Fläche und der Umfang immer stimmen, müssen sie in jeder " "Elementfunktion aktualisiert werden. Das ist aber aufwendiger " "als die Lösung mit den Funktionen."; Memo1->Lines->Add("1. a)"); Memo1->Lines->Add(s1a); Memo1->Lines->Add("1. b)"); Memo1->Lines->Add(s1b); MeinString s("abcd"); s.replace(2,"12345"); Memo1->Lines->Add("2."); Memo1->Lines->Add(s2b1); Memo1->Lines->Add(s2b2); }
12.7.2 Aufgabe 8.2.2
// Datei: d:\Loesungen\kap-8\8.2\Aufg2U.cpp
// --- Aufgabe 1 ----------------------------------------------------class E { public: E() { Form1->Memo1->Lines->Add("Standardkonstruktor"); } E(int) { Form1->Memo1->Lines->Add("int-Konstruktor"); } }; class C { E e1,e2; public: C() { } C(int i):e1(i) { } C(int i,int j):e1(i),e2(j) { } }; void Konstruktoren() { // Die folgenden Definitionen erzeugen die Meldungen: C c0; // Standardkonstruktor C c1(1); // Standardkonstruktor // int-Konstruktor C c2(1,2); // Standardkonstruktor // int-Konstruktor // int-Konstruktor Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
317
318
12 Lösungen
} // --- Aufgabe 2 ----------------------------------------------------class C2DPunkt{ public: // Für CRechteck1 müssen x und y public sein double x,y; public: C2DPunkt(const double& x_, const double& y_):x(x_),y(y_) { } C2DPunkt(const C2DPunkt& pos):x(pos.x),y(pos.y) { } // Später wird gezeigt, daß dieser zweite Konstruktor ein // Copy-Konstruktor ist, der automatisch erzeugt wird und deswegen nicht // definiert werden muß. AnsiString toStr() { return "("+FloatToStr(x)+"|"+FloatToStr(y)+")"; }// { z. B. (2,345|3,45678) } }; class C2DKreis{ double r; C2DPunkt Position; public: C2DKreis(double Radius=1, double x=0, double y=0): r(Radius), Position(x,y) { } // Reihenfolge der Deklaration C2DKreis(double Radius, C2DPunkt pos): r(Radius), Position(pos) { } // Reihenfolge der Deklaration /* Alternativ zu diesen beiden Konstruktoren sind auch die folgenden möglich. Beim zweiten dieser Konstruktoren wird ein temporäres Objekt als Default-Argument verwendet: C2DKreis(double Radius, double x, double y): r(Radius), Position(x,y) { } // Reihenfolge der Deklaration C2DKreis(double Radius=0, C2DPunkt pos=C2DPunkt(0,0)): r(Radius), Position(pos) { } // Reihenfolge der Deklaration */ AnsiString toStr() { return "Kreis mit Radius "+FloatToStr(r)+" in "+Position.toStr(); } }; void Kreis() { C2DKreis k1(1,2,3); Form1->Memo1->Lines->Add(k1.toStr()); C2DKreis k2(4); Form1->Memo1->Lines->Add(k2.toStr()); C2DKreis k3; Form1->Memo1->Lines->Add(k3.toStr()); C2DPunkt p(6,7); C2DKreis k5(5,p); Form1->Memo1->Lines->Add(k5.toStr()); C2DKreis k6(8,C2DPunkt(9,10)); Form1->Memo1->Lines->Add(k6.toStr()); } // --- Aufgabe 3 ----------------------------------------------------class CRechteck1 { C2DPunkt LinksOben; // Eckpunkt links oben Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
double a,b; // Seitenlängen public: CRechteck1(C2DPunkt Mittelpunkt, double a_, double b_):a(a_), b(b_), LinksOben(Mittelpunkt.x-a/2, Mittelpunkt.y-b/2){ } AnsiString toStr() { return FloatToStr(LinksOben.x)+"|"+FloatToStr(LinksOben.y); } AnsiString Kommentar() { return "Die Elemente eines Objekts werden in der Reihenfolge initialisiert, " "in der sie in der Klasse aufgeführt werden. Deshalb wird zuerst " "LinksOben initialisiert, und dann erst a und b. Linksoben verwendet " "dazu die bisher nicht initialisierten Werte a und b.\n\n" "Wenn man die Elemente in einer Initialisiererliste in der Reihenfolge " "ihrer Definition in der Klasse aufführt, ist die Gefahr eines solchen " "Missverständnisses geringer.\n\n" "Die fehlerhafte Initialisierung in der Klasse CRechteck1 vermeidet " "man, indem man die Reihenfolge der Datenelemente ändert."; } }; class CRechteck2 { // Reihenfolge der Deklarationen vertauscht double a,b; // Seitenlängen C2DPunkt LinksOben; // Eckpunkt links oben public: CRechteck2(C2DPunkt Mittelpunkt, double a_,double b_): a(a_),b(b_), LinksOben(Mittelpunkt.x-a/2, Mittelpunkt.y-b/2){ } AnsiString toStr() { return FloatToStr(LinksOben.x)+"|"+ FloatToStr(LinksOben.y); } }; void Rechteck1() { C2DPunkt mp(100,100); CRechteck1 r1(mp, 10, 20); Form1->Memo1->Lines->Add(r1.toStr()); Form1->Memo1->Lines->Add(r1.Kommentar()); CRechteck2 r2(mp, 10, 20); Form1->Memo1->Lines->Add(r2.toStr()); } // --- Aufgabe 4 ----------------------------------------------------void MeinString_Konstr() { Form1->Memo1->Lines->Add( "Da dieser Standardkonstruktor die anderen Konstruktoren ergänzen soll, " "kann im Destruktor nicht mehr entschieden werden, ob der reservierte " "Speicher mit delete oder mit delete[] freigegeben werden soll." ); }
12.7.3 Aufgabe 8.2.4
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
319
320
12 Lösungen
// Datei: d:\Loesungen\kap-8\8.2\Aufg4U.cpp
//--- Aufgabe 1 -----------------------------------------------------/* Für Zähler und Nenner über 7 liegen bei diesen Rechnungen Zwischenergebnisse außerhalb des Bereichs von int. Mit dem Datentyp __int64 anstelle von int können diese Rechnungen auf Zähler und Nenner bis 14 erweitert werden. */ // c) int ggT(int x, int y) { if (x<0) x=-x; if (y<0) y=-y; while (y != 0) { int r = x%y; x = y; y = r; } return x; // ggT(x,y)==x; } class CBruch { int z,n; // z: Zähler, n: Nenner public: CBruch(int z_, int n_=1):z(z_),n(n_) { int t = ggT(z,n); if (t==0) { ShowMessage("t=0"); return; } z=z/t; n=n/t; }; friend inline bool operator==(const CBruch& q1, const CBruch& q2); friend inline bool operator<(const CBruch& q1, const CBruch& q2); friend CBruch operator+(const CBruch& p, const CBruch& q); friend CBruch operator-(const CBruch& p, const CBruch& q); friend CBruch operator*(const CBruch& p, const CBruch& q); friend CBruch operator/(const CBruch& p, const CBruch& q); AnsiString toStr() { // Mit __int64 FloatToStr anstelle IntToStr return FloatToStr(z)+"/"+FloatToStr(n); } }; // a) inline bool operator==(const CBruch& q1, const CBruch& q2) { return q1.z*q2.n==q2.z*q1.n; } // Die folgenden Anweisungen wurden mit leichten Änderungen aus // include\utility.h übernommen. Die Definitionen aus utility.h // gehören zum C++-Standard. // Da die folgende Funktion nicht auf die Elemente von CBruch zugreift, // muß sie kein friend sein: inline bool operator!=(const CBruch& x, const CBruch& y) { return !(x == y); } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
// b) inline bool operator<(const CBruch& q1, const CBruch& q2) { return q1.z*q2.n < q2.z*q1.n; } // Auch die folgenden Anweisungen wurden mit leichten Änderungen // aus utility übernommen. inline bool operator>(const CBruch& x, const CBruch& y) { return y < x; } inline bool operator<=(const CBruch& x, const CBruch& y) { return !(y < x); } inline bool operator>=(const CBruch& x, const CBruch& y) { return !(x < y); }; CBruch operator+(const CBruch& p, const CBruch& q) { int g=ggT(p.n,q.n); return CBruch(p.z*(q.n/g) + q.z*(p.n/g) , p.n*(q.n/g)); }; CBruch operator-(const CBruch& p, const CBruch& q) { int g=ggT(p.n,q.n); return CBruch(p.z*(q.n/g) - q.z*(p.n/g) , p.n*(q.n/g)); }; CBruch operator*(const CBruch& p, const CBruch& q) { return CBruch (p.z*q.z,p.n*q.n); }; CBruch operator/(const CBruch& p, const CBruch& q) { return CBruch(p.z*q.n,p.n*q.z); }; // d) const CBruch Eins(1,1); CBruch Test_geom_Reihe1(CBruch p,int n) { // summiere die Potenzen: 1 + p + p^2 ... + p^n CBruch s=Eins, q=Eins; for (int i=1; i<=n; i++) { q=q*p; s=s+q; } return s; } CBruch Test_geom_Reihe2(CBruch p,int n) { // Summenformel: (p^(n+1) - 1)/(p-1) CBruch q=Eins; for (int i=1; i<=n+1; i++) q=q*p; // q=p^(n+1) return (q-Eins)/(p-Eins); } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
321
322
int pot(int x, int n) { int p=1; for (int i=0; iMemo1->Lines->Add("p="+p.toStr()+" g1="+g1.toStr()+ " g2="+g2.toStr()+ " x="+x.toStr()); } } } //--- Aufgabe 2 -----------------------------------------------------class AssozContainer { // Dieser Container ist eine starke Vereinfachung und // zeigt nur, wie man den Indexoperator so überladen kann, // dass er sich wie in std::map verhält. int n; // Anzahl der Elemente im Container typedef AnsiString T1; typedef AnsiString T2; public: struct Paar { T1 first; T2 second; }; Paar a[100]; // nur zur Vereinfachung so einfach AssozContainer():n(0) {}; // a) T1& operator[](T2 s) { // lineares Suchen: sehr einfach for (int i=0; iMemo1->Lines->Add(a[i].first+": "+a[i].second); } // b) class iterator { Paar* Position; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
public: iterator(Paar* pos):Position(pos) { } bool operator!= (const iterator& y) { return Position != y.Position; } iterator& operator++(int) { Position++; return *this; } Paar& operator* () { return *Position; } Paar* operator-> () { return Position; } }; iterator begin() { return a; } iterator end() { return a+n; } }; void test_AssozCont() { AssozContainer a; Form1->Memo1->Lines->Add("1. "); a.show_all(); a["Luigi Mafiosi"]="[email protected]"; Form1->Memo1->Lines->Add("2. "); a.show_all(); a["Karl Erbschleicher"]="[email protected]"; Form1->Memo1->Lines->Add("3. "); a.show_all(); a["Luigi Mafiosi"]="[email protected]"; // palermo.net war zu langsam Form1->Memo1->Lines->Add("4. "); a.show_all(); Form1->Memo1->Lines->Add(a["Karl Erbslaicher"]); // Schreibfehler Form1->Memo1->Lines->Add("5. "); a.show_all(); }
12.7.4 Aufgabe 8.2.6
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
323
324
12 Lösungen
// Datei: d:\Loesungen\kap-8\8.2\Aufg6U.cpp
// --- Aufgabe 1 ----------------------------------------------------void SpezEltFktNotwendig() { const int n=10; AnsiString a[n]={ "a) Nicht notwendig, da alle Klassen keine Zeiger enthalten.", "b) Nicht notwendig, da CBruch keine Zeiger enthält.", "c) Notwendig, da alle Klassen Zeiger enthalten.", "d) Mit string anstelle char* ist keine dieser Funktionen notwendig.", "e) Mit char[100] anstelle char* ist keine dieser Funktionen notwendig.", "f) Falls das Array mit new angelegt wird, sind diese Funktionen notwendig.", "g) Mit einem vector sind diese Funktionen nicht notwendig.", "Diese Beispiele zeigen insbesondere, dass man sich viel Arbeit " "sparen kann, wenn man Stringklassen anstelle von nullterminierten " "Strings verwendet.", "", "Wenn man alles richtig macht, ist es kein Fehler, diese Funktionen " "selbst zu definieren, obwohl sie auch vom Compiler erzeugt werden. " "Allerdings haben die selbst definierten Funktionen keine Vorteile " "gegenüber den vom Compiler erzeugten. Sie haben aber den Nachteil, " "dass sie bei jeder Änderung der Klasse angepasst werden müssen, " "was leicht vergessen werden kann. Am besten ist es, wenn man alle" "Klassen ohne Zeiger definieren kann."}; for (int i=0; iMemo1->Lines->Add(a[i]); } // --- Aufgabe 2 ----------------------------------------------------void display(AnsiString s, int i=-1) { if (i>=0) s=s+IntToStr(i); Form1->Memo1->Lines->Add(s); } class C{ int x; public: C (int x_=0) { // Beim Aufruf ohne Argument ein Standardx=x_; // konstruktor display(" Konstruktor: ", x); } C (const C& c) { x=c.x; display(" Kopierkonstruktor: ", x); } C& operator=(const C& c) { x=c.x; display(" operator=: ", x); return *this; } ~C () { display(" }
Destruktor: ",x);
friend int f3(C c); friend int f4(const C& c); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
325
friend C operator+(const C& c1,const C& c2); }; C f1(int i) { return C(i); } C f2(int i) { C tmp(i); return tmp; } int f3(C c) { return c.x; } int f4(const C& c) { return c.x; } void test1() { display("vor C x=C(1)"); C x=C(1); // Konstruktor: 1 C y=x; // Kopierkonstruktor: display("vor x=y"); x=y; // operator=: 1 display("vor C z(x)"); C z(x); // Kopierkonstruktor: display("vor f1(2)"); f1(2); // Konstruktor: 2 // Destruktor: 2 display("vor f2(3)"); f2(3); // Konstruktor: 3 // Kopierkonstruktor: // Destruktor: 3 // Destruktor: 3 display("vor f3(4)"); f3(4); // Konstruktor: 4 // Destruktor: 4 display("vor f3(x)"); f3(x); // Kopierkonstruktor: // Destruktor: 1 display("vor f4(4)"); f4(4); // Konstruktor: 4 // Destruktor: 4 display("vor f4(x)"); f4(x); // keine Ausgabe display("Ende von test1"); } // Destruktor: 1 // Destruktor: 1 // Destruktor: 1
1
1
3
1
class D { C c1; C c2; }; void test2() { display("vor D d1"); D d1; // Konstruktor: 0 Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
326
// Konstruktor: 0 display("vor D d2=d1"); D d2=d1; // Kopierkonstruktor: 0 // Kopierkonstruktor: 0 display("vor d2=d1"); d2=d1; // operator=: 0 // operator=: 0 display("nach d2=d1"); } // Destruktor: 0 // Destruktor: 0 // Destruktor: 0 // Destruktor: 0
// --- Aufgabe 3 ----------------------------------------------------class CBruch { int z,n; // z: Zähler, n: Nenner public: CBruch(int z_, int n_=1):z(z_),n(n_){ int t = ggT(z,n); // ggT: siehe Aufgabe 8.2.4 if (t==0) { ShowMessage("t=0"); return; } z=z/t; n=n/t; }; friend inline bool operator==(const CBruch& q1, const CBruch& q2); friend inline bool operator<(const CBruch& q1, const CBruch& q2); CBruch& operator+=(const CBruch& q) { int g=ggT(n,q.n); z=z*(q.n/g) + q.z*(n/g); n=n*(q.n/g); return *this; }; CBruch& operator-=(const CBruch& q) { int g=ggT(n,q.n); z=z*(q.n/g) - q.z*(n/g); n=n*(q.n/g); return *this; }; CBruch& operator*=(const CBruch& q) { z=z*q.z; n=n*q.n; return *this; }; CBruch& operator/=(const CBruch& q) { z=z*q.n; n=n*q.z; return *this; }; AnsiString toStr() { // Mit __int64 FloatToStr anstelle IntToStr return IntToStr(z)+"/"+IntToStr(n); } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
}; // a) inline bool operator==(const CBruch& q1, const CBruch& q2) { return q1.z*q2.n==q2.z*q1.n; } // Die folgenden Anweisungen wurden mit leichten Änderungen aus // utility übernommen. Die Definitionen aus utility gehören // zum C++-Standard: inline bool operator!=(const CBruch& x, const CBruch& y) { return !(x == y); } // b) inline bool operator<(const CBruch& q1, const CBruch& q2) { return q1.z*q2.n < q2.z*q1.n; } // Auch die folgenden Anweisungen wurden mit leichten Änderungen // aus utility übernommen. inline bool operator>(const CBruch& x, const CBruch& y) { return y < x; } inline bool operator<=(const CBruch& x, const CBruch& y) { return !(y < x); } inline bool operator>=(const CBruch& x, const CBruch& y) { return !(x < y); }; inline CBruch& operator+(const CBruch& p, const CBruch& q) { CBruch tmp=p; return tmp+=q;; }; inline CBruch& operator-(const CBruch& p, const CBruch& q) { CBruch tmp=p; return tmp-=q;; }; inline CBruch operator*(const CBruch& p, const CBruch& q) { CBruch tmp=p; return tmp*=q;; }; inline CBruch operator/(const CBruch& p, const CBruch& q) { CBruch tmp=p; return tmp/=q;; }; // d) CBruch Test_geom_Reihe1(CBruch p,int n) { // summiere die Potenzen: 1 + p + p^2 ... + p^n CBruch s(1), q(1); for (int i=1; i<=n; i++) { Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
327
328
12 Lösungen
q=q*p; s=s+q; } return s; } CBruch Test_geom_Reihe2(CBruch p,int n) { // Summenformel: (p^(n+1) - 1)/(p-1) CBruch q(1); for (int i=1; i<=n+1; i++) q=q*p; // q=p^(n+1) return (q-CBruch(1))/(p-CBruch(1)); } int pot(int x, int n) { int p=1; for (int i=0; iMemo1->Lines->Add("p="+p.toStr()+" g1="+g1.toStr()+ " g2="+g2.toStr()+ " x="+x.toStr()); } } } // --- Aufgabe 4 ----------------------------------------------------void MemCpy() { AnsiString s="Das kann zu flachen Kopien führen."; Form1->Memo1->Lines->Add(s); } // --- Aufgabe 5 ----------------------------------------------------#include "\Loesungen\CppUtils\TimeUtils.cpp" class C1 { public: C1():x(0),y(0) {} C1(int a):x(a),y(a) {} int x,y; }; void test_PrefixPostfixClick() { const int n=100000; typedef vector T; T v(n); for (int i=0; i
12.7 Lösungen Kapitel 8
329
CHRTimer t1,t2; int s=0; t1.Start(); for (T::iterator i=v.begin(); i!=v.end(); i++) s+=*i;; t1.End(); t2.Start(); for (T::iterator i=v.begin(); i!=v.end(); ++i) s+=*i;; t2.End(); CHRTimer t10,t20; t10.Start(); for (int i=0; iMemo1->Lines->Add("Postfix:"+t1.TimeStr()); Form1->Memo1->Lines->Add("Präfix: "+t2.TimeStr()); Form1->Memo1->Lines->Add("int Postfix:"+t10.TimeStr()); Form1->Memo1->Lines->Add("int Präfix: "+t20.TimeStr()); /* Projekt|Optionen|Compiler: Endgültige Version Mit "typedef vector T;" ergaben sich die folgenden Zeiten: Postfix:0,00211703179738179 Präfix: 0,00133927823128111 Postfix:0,00205333646222699 Präfix: 0,00133927823128111 Mit "typedef vector T;" sind dagegen überraschenderweise die Postfix-Operationen schneller: Postfix:0,00133927823128111 Präfix: 0,0020365745319231 Postfix:0,00140548785598149 Präfix: 0,00200640305737609 */ } // --- Aufgabe 6 ----------------------------------------------------class Festkomma64 { __int64 i; public: Festkomma64(): i(0) { } Festkomma64(int i_): i(i_*10000) { } Festkomma64(double d_): i((d_+0.000005)*10000) { } Festkomma64(const Festkomma64& f_): i(f_.i) { } AnsiString toStr() { AnsiString s=FloatToStr(i); while (s.Length()<5) s = "0"+s; s.Insert(",",s.Length()-3); return s; } Festkomma64& operator+=(Festkomma64 a) Festkomma64& operator-=(Festkomma64 a)
{i+=a.i; return *this;} {i-=a.i; return *this; }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
330
Festkomma64& operator*=(Festkomma64 a) Festkomma64& operator/=(Festkomma64 a)
12 Lösungen
{i*=a.i; i /= 10000; return *this; } {i*=10000; i/=a.i; return *this; }
friend bool operator<=(Festkomma64 a,Festkomma64 b); }; bool operator<=(Festkomma64 a,Festkomma64 b) { return a.i<=b.i; } Festkomma64 operator+(Festkomma64 a,Festkomma64 b) { return a+=b; } Festkomma64 operator*(Festkomma64 a,Festkomma64 b) { return a*=b; } Festkomma64 operator-(Festkomma64 a,Festkomma64 b) { return a-=b; } Festkomma64 operator/(Festkomma64 a,Festkomma64 b) { return a/=b; } Festkomma64 est(Festkomma64 x) { // Einkommensteuertarif nach § 32a (1) Festkomma64 est; x=(x/54)*54; if (x <= 12365) est = 0; else if (x <= 58643) { Festkomma64 y = (x-12312)/10000; est = (91.19*y+2590)*y; } else if (x <= 120041) { Festkomma64 z = (x-58590)/10000; est = (151.91*z + 3434)*z +13938; } else est = 0.53*x -22843; return 10000*(est/10000); }; // Der Datentyp Currency ist im wesentlichen so definiert wie hier der // Datentyp Festkomma64. //typedef Festkomma64 T; typedef Currency T; T Est(T x) { // Einkommensteuertarif nach § 32a (1) T est; x=(x/54)*54; if (x <= 12365) est = 0; else if (x <= 58643) { T y = (x-12312)/10000; est = (91.19*y+2590)*y; } else if (x <= 120041) { Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
331
T z = (x-58590)/10000; est = (151.91*z + 3434)*z +13938; } else est = 0.53*x -22843; return 10000*(est/10000); }; void EstVergl(Festkomma64 a,Festkomma64 b) { Form1->Memo1->Lines->Add(a.toStr()+": "+est(a).toStr()+" Soll="+b.toStr()); } void test_Est() { EstVergl(10800,0); EstVergl(16200,1020); EstVergl(21600,2484); EstVergl(27000,4000); EstVergl(32400,5570); EstVergl(37800,7193); EstVergl(43200,8870); EstVergl(48600,10599); EstVergl(54000,12381); EstVergl(59400,14217); EstVergl(64800,16129); EstVergl(70200,18129); EstVergl(75600,20218); EstVergl(81000,22396); EstVergl(86400,24663); EstVergl(91800,27018); EstVergl(97200,29461); EstVergl(102600,31994); EstVergl(108000,34615); EstVergl(113400,37324); EstVergl(118800,40123); } // --- Aufgabe 7 ----------------------------------------------------void KonstrAufrufeVermeiden() { AnsiString s="Indem man sie private deklariert. "; Form1->Memo1->Lines->Add(s); } // --- Aufgabe 8 ----------------------------------------------------C operator+(const C& c1,const C& c2) { // friend in der Klasse C display(" operator+: ", c1.x+c2.x); return C(c1.x+c2.x); } void test3() { C s1,s2,s3,s4; display("1. vor + s1=C(1)+C(3); display("2. vor + C x=C(5),y=C(7); s2=x+y; display("3. vor + s3=C(9); s3=s3+C(11); display("4. vor +
"); "); "); ");
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
332
12 Lösungen
s4=x; s4=s4+y; display("Ende von test"); }
12.7.5 Aufgabe 8.2.13
// Datei: d:\Loesungen\kap-8\8.2\CQuadrat.h
#ifndef CQuadratH #define CQuadratH class CQuadrat{ // Elementfunktionen außerhalb definieren double a; public: CQuadrat(double Seitenlaenge):a(Seitenlaenge){}; double Seitenlaenge() const; void setze_Seitenlaenge(double Seitenlaenge); double Flaeche() const; double Umfang() const; AnsiString toStr() const; }; #endif
12.7.6 Aufgabe 8.2.13
// Datei: d:\Loesungen\kap-8\8.2\CQuadrat.cpp
namespace N_Aufg13U { #include "CQuadrat.h" double CQuadrat::Seitenlaenge() const { return a; } void CQuadrat::setze_Seitenlaenge(double Seitenlaenge) { a=Seitenlaenge; } double CQuadrat::Flaeche() { return a*a; }
const
double CQuadrat::Umfang() { return 4*a; }
const
AnsiString CQuadrat::toStr() const { Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
return "Quadrat mit Seitenlänge "+FloatToStr(a); } } // end of namespace N_Aufg13U
12.7.7 Aufgabe 8.2.13
// Datei: d:\Loesungen\kap-8\8.2\Aufg13U.cpp
//----- Aufgabe 1 und 2 ---------------------------------------------#include "CQuadrat.h" #include "CKreis.h" // "CQuadrat.cpp" und "CKreis.cpp" werden mit "Projekt|Dem Projekt // hinzufügen" dem Projekt hinzugefügt void Aufg1_2() { CQuadrat q(10); Form1->Memo1->Lines->Add(q.toStr()); Form1->Memo1->Lines->Add(q.Umfang()); Form1->Memo1->Lines->Add(q.Flaeche()); CQuadrat* pq=new CQuadrat(10); Form1->Memo1->Lines->Add(pq->toStr()); Form1->Memo1->Lines->Add(pq->Umfang()); Form1->Memo1->Lines->Add(pq->Flaeche()); CKreis k(10); Form1->Memo1->Lines->Add(k.toStr()); Form1->Memo1->Lines->Add(k.Umfang()); Form1->Memo1->Lines->Add(k.Flaeche()); CKreis* pk=new CKreis(10); Form1->Memo1->Lines->Add(pk->toStr()); Form1->Memo1->Lines->Add(pk->Umfang()); Form1->Memo1->Lines->Add(pk->Flaeche()); } //----- Aufgabe 3 ---------------------------------------------------void Aufg_3() { /* 3. Bei der Änderung einer Klassendefinition (also insbesondere der in der Klasse definierten inline-Funktion) muss der Compiler alle Dateien übersetzen, die diese Datei mit #include verwenden. Wenn dagegen die Funktionsdefinition einer außerhalb der Klasse definierten Funktion geändert wird, muss nur diese Datei neu kompiliert werden. Dieser geringere Aufwand beim Kompilieren hat allerdings eine längere Laufzeit des Programms zur Folge, da die Funktionen dann nicht mehr inline sind. Auch wenn diese Überlegungen prinzipiell eher gegen als für inlineFunktionen sprechen, können diese Nachteile oft vernachlässigt werden. Bei inline-Funktionen mit wenig Anweisungen kann der bei einer Expansion erzeugte Code sogar kleiner sein als der für einen Funktionsaufruf. */ } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
333
334
12 Lösungen
//----- Aufgabe 4 ---------------------------------------------------class C { static int count_; public: static int count() { return count_; } void* operator new(size_t size) { count_++; return ::new C; }; void* operator new[](size_t size) { count_++; return ::operator new(size); }; void operator delete(void *p) { count_--; ::delete(p); }; void operator delete[](void *p) { count_--; ::delete[](p); }; }; int C::count_=0; void Aufg_4() { C* p1=new C; delete p1; C* p2=new C[10]; delete[] p2; } //----- Aufgabe 5 ---------------------------------------------------class Singleton { static Singleton* instance_; public: static Singleton* Instance(); int data; // besser private und Zugriffsfunktionen private: // protected, falls Singleton als Basisklasse verwendet werden soll Singleton(){data=17;}; }; Singleton* Singleton::Instance() { if (instance_==0) instance_=new Singleton(); return instance_; }; Singleton* Singleton::instance_=0; Singleton* p1=Singleton::Instance(); void Aufg_Singleton() { Singleton* p2=Singleton::Instance(); // p2->data=19; Form1->Memo1->Lines->Add(IntToStr(p1->data)); // p1->data=19 Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
// falls p1 ein gewöhnliches globales Objekt wäre, hätte die // globale Variable den Wert 17 }
12.7.8 Aufgaben 8.3
// Datei: d:\Loesungen\kap-8\8.3\VererbU.cpp
#include "VererbU.h" //-------------------------------------------------------------------// Aufgaben 8.3.5 //-------------------------------------------------------------------//------ Aufgabe 1 --------------------------------------------------class C { int i,j; public: C(int x,int y): i(x),j(y) { Form1->Memo1->Lines->Add("Konstruktor C"); } C(): i(0),j(0) { Form1->Memo1->Lines->Add("Standardkonstruktor C"); } ~C() { Form1->Memo1->Lines->Add("Destruktor C"); } }; class D : public C { int k,a,b; C c; public: D(int x):C(x,17),a(x),b(0),c(x,1),k(19) { Form1->Memo1->Lines->Add("Konstruktor-1 D"); } D(int x,int y, int z):C(x,y),a(1),b(2),c(x,y),k(z) { Form1->Memo1->Lines->Add("Konstruktor-2 D"); } ~D() { Form1->Memo1->Lines->Add("Destruktor D"); } }; class E : public D { int m; C c; public: E(int x,int y, int z):D(x,3,z),m(z) { Form1->Memo1->Lines->Add("Konstruktor E"); } ~E() { Form1->Memo1->Lines->Add("Destruktor E"); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
335
336
12 Lösungen
} }; void __fastcall { C c(1,2); // D d(1,2,3); // // // E e(1,2,3); // // // // //
TForm1::Aufg_1Click(TObject *Sender)
}
Destruktor Destruktor Destruktor Destruktor Destruktor Destruktor Destruktor Destruktor Destruktor
// // // // // // // // //
Konstruktor C Konstruktor C Konstruktor C Konstruktor-2 D Konstruktor C Konstruktor C Konstruktor-2 D Standardkonstruktor C Konstruktor E E C D C C D C C C
//------ Aufgabe 2 --------------------------------------------------class C1DPunkt { protected: // private wäre besser. Dazu wäre aber eine Funktion notwendig, double x; // die C1DPunkt::x zurückgibt AnsiString toStr() { return FloatToStr(x); } public: C1DPunkt(double x_):x(x_) { } void display() { Form1->Memo1->Lines->Add(toStr()); } }; class C2DPunkt:public C1DPunkt { protected: // private wäre besser double y; AnsiString toStr() { return FloatToStr(x)+"|"+FloatToStr(y); } public: C2DPunkt(double x_,double y_):C1DPunkt(x_),y(y_) void display() { Form1->Memo1->Lines->Add(toStr()); } };
{ }
class C3DPunkt : public C2DPunkt { double z; AnsiString toStr() { return FloatToStr(x)+"|"+FloatToStr(y)+"|"+FloatToStr(z); } public: C3DPunkt(double x_,double y_,double z_):C2DPunkt(x_,y_),z(z_) { void display() {
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
Form1->Memo1->Lines->Add(toStr()); } }; void test_Punkte() { C1DPunkt p1(1); C2DPunkt p2(1,2); C3DPunkt p3(1,2,3); p1.display(); p2.display(); p3.display(); } void __fastcall TForm1::Aufg_2Click(TObject *Sender) { test_Punkte(); } //------ Aufgabe 3 --------------------------------------------------// a) // Es liegt nahe, alle gemeinsamen Elemente der Klassen in // einer gemeinsamen Basisklasse zusammenzufassen: class CImmobilie { protected: // Nur zur Vereinfachung - private wäre besser char* Anschrift; double Kaufpreis; public: CImmobilie(char* Anschrift_, double Kaufpreis_):Kaufpreis(Kaufpreis_) { Anschrift = new char[strlen(Anschrift_)+1]; strcpy(Anschrift,Anschrift_); } ~CImmobilie() { delete[] Anschrift; } AnsiString toStr() const { return " in "+AnsiString(Anschrift)+ " KP: "+FloatToStr(Kaufpreis) + " DM "; } CImmobilie(const CImmobilie& c); // für b) CImmobilie& operator=(const CImmobilie& rhs); // für b) }; void display(const CImmobilie* i, TMemo* Memo) { Memo->Lines->Add(i->toStr()); } class CGrundstueck : public CImmobilie { double Flaeche; public: CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_): CImmobilie(Anschrift_,Kaufpreis_),Flaeche(Flaeche_) { } AnsiString toStr() const { return "Grundstück"+CImmobilie::toStr()+ " Fläche: "+FloatToStr(Flaeche)+ " qm"; } }; void display(const CGrundstueck* i, TMemo* Memo) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
337
338
12 Lösungen
{ Memo->Lines->Add(i->toStr()); } class CWohnImmobilie : public CImmobilie { // Die Notwendigkeit für diese Klasse ergibt sich nicht direkt aus der // Aufgabenstellung. protected: // Nur zur Vereinfachung - private wäre besser double Wohnflaeche; int AnzahlZimmer; public: CWohnImmobilie(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, int AnzahlZimmer_): CImmobilie(Anschrift_,Kaufpreis_), Wohnflaeche (Wohnflaeche_), AnzahlZimmer(AnzahlZimmer_){} AnsiString toStr() const { return CImmobilie::toStr()+" Wohnfläche: "+ FloatToStr(Wohnflaeche)+" qm Zimmer: "+IntToStr(AnzahlZimmer); } }; void display(const CWohnImmobilie* i, TMemo* Memo) { Memo->Lines->Add(i->toStr()); } class CEigentumswohnung : CWohnImmobilie { public: CEigentumswohnung(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, int AnzahlZimmer_): CWohnImmobilie(Anschrift_, Kaufpreis_, Wohnflaeche_, AnzahlZimmer_){} AnsiString toStr() const { return "ETW"+CWohnImmobilie::toStr(); } }; void display(const CEigentumswohnung* i, TMemo* Memo) { Memo->Lines->Add(i->toStr()); } class CEinfamilienhaus :CWohnImmobilie { double Grundstuecksgroesse; public: CEinfamilienhaus(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, double Grundstuecksgroesse_, int AnzahlZimmer_): CWohnImmobilie(Anschrift_, Kaufpreis_, Wohnflaeche_, AnzahlZimmer_), Grundstuecksgroesse(Grundstuecksgroesse_){} AnsiString toStr() const { return "EFH"+CWohnImmobilie::toStr(); } }; void display(const CEinfamilienhaus* i, TMemo* Memo) { Memo->Lines->Add(i->toStr()); } class CGewerbeobjekt : CImmobilie { double Nutzflaeche; char* Nutzungsart; // z. B. "Büro", "Fabrik" public: CGewerbeobjekt(char* Anschrift_, double Kaufpreis_, double Nutzflaeche_, char* Nutzungsart_):CImmobilie(Anschrift_,Kaufpreis_), Nutzflaeche(Nutzflaeche_) { Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
339
Nutzungsart=new char[strlen(Nutzungsart_)+1]; strcpy(Nutzungsart,Nutzungsart_); } ~CGewerbeobjekt() { delete[] Nutzungsart; } AnsiString toStr() const { return "Gewerbeobjekt"+ CImmobilie::toStr()+ " Nutzfläche: "+FloatToStr(Nutzflaeche)+" qm Nutzungsart: "+Nutzungsart; } CGewerbeobjekt(const CGewerbeobjekt& c); // für b) CGewerbeobjekt& operator=(const CGewerbeobjekt& rhs); // für b) }; void display(const CGewerbeobjekt* i, TMemo* Memo) { Memo->Lines->Add(i->toStr()); } void test() { // CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_) CGrundstueck g("Tübingen",100000,400); display(&g, Form1->Memo1); // CEigentumswohnung(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, int AnzahlZimmer_) CEigentumswohnung etw("Stuttgart",500000,120,5); display(&etw, Form1->Memo1); // CEinfamilienhaus(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, double Grundstuecksgroesse_, int AnzahlZimmer_) CEinfamilienhaus efh("Kiel",300000,150,900,5); display(&efh, Form1->Memo1); // CGewerbeobjekt(char* Anschrift_, double Kaufpreis_, double Nutzflaeche_, char* Nutzungsart_) CGewerbeobjekt gew("München",400000,200,"Lagerhalle"); display(&gew, Form1->Memo1); } // b) AnsiString b= "Alle Klassen, die Zeiger enthalten, benötigen einen Destruktor, " "Copy-Konstruktor und Zuweisungsoperator."; CImmobilie::CImmobilie(const CImmobilie& x):Kaufpreis(x.Kaufpreis) { Anschrift = new char[strlen(x.Anschrift)+1]; strcpy(Anschrift,x.Anschrift); } CImmobilie& CImmobilie::operator=(const CImmobilie& rhs) { if (this!=&rhs) { delete Anschrift; Anschrift = new char[strlen(rhs.Anschrift)+1]; strcpy(Anschrift,rhs.Anschrift); Kaufpreis=rhs.Kaufpreis; } return *this; }; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
340
12 Lösungen
CGewerbeobjekt::CGewerbeobjekt(const CGewerbeobjekt& x):CImmobilie(x),Nutzflaeche(x.Nutzflaeche) { Nutzungsart= new char[strlen(x.Nutzungsart)+1]; strcpy(Nutzungsart,x.Nutzungsart); } CGewerbeobjekt& CGewerbeobjekt::operator=(const CGewerbeobjekt& rhs) { if (this!=&rhs) { CImmobilie::operator=(rhs); // Aufruf von this->C::operator= delete Nutzungsart; Nutzungsart = new char[strlen(rhs.Nutzungsart)+1]; strcpy(Nutzungsart,rhs.Nutzungsart); Kaufpreis=rhs.Kaufpreis; } return *this; }; // c) AnsiString c= "Diese Hierarchie ist einfacher als die Definition der einzelnen Klassen, " "bei denen keine Elemente wiederverwendet werden. In dieser Hierarchie " "ist auch nur für die Klassen ein explizit definierter Copy-Konstruktor, " "Zuweisungsoperator und Destruktor notwendig, die Zeiger enthalten. Bei " "Klassen, die Zeiger aus den Basisklassen erben, reicht der Aufruf dieser " "Funktionen aus der Basisklasse aus. "; // d) AnsiString d1= "Wenn diese Klassen Strings der Klassen string bzw. AnsiString statt " "Zeigern auf nullterminierte Strings enthalten, ist für keine dieser " "Klassen ein explizit definierter Copy-Konstruktor, Zuweisungsoperator " "und Destruktor notwendig."; AnsiString d2= "Das erspart die Definition dieser Funktionen und damit viel Arbeit. " "Deshalb sollte man auf Klassen mit Zeigern möglichst verzichten und " "Stringklassen anstelle von Zeigern auf nullterminierte Strings verwenden."; void __fastcall TForm1::Aufg_3Click(TObject *Sender) { test(); Memo1->Lines->Add("b)"); Memo1->Lines->Add(b); Memo1->Lines->Add("c)"); Memo1->Lines->Add(c); Memo1->Lines->Add("d)"); Memo1->Lines->Add(d1); Memo1->Lines->Add(""); Memo1->Lines->Add(d2); } //------ Aufgabe 4 --------------------------------------------------namespace N_Aufgabe_4 { // a) class CQuadrat1{ double a; public: CQuadrat1(double a_):a(a_) {} Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
}; class CRechteck1:public CQuadrat1{ double b; public: CRechteck1(double a_, double b_):CQuadrat1(a_),b(b_) {} }; // b) class CRechteck2{ double a,b; public: CRechteck2(double a_,double b_):a(a_),b(b_){}; }; class CQuadrat2:public CRechteck2 { public: CQuadrat2(double a_):CRechteck2(a_,a_) {} }; // c) // Bei der Hierarchie b) benötigt jedes Quadrat doppelt soviel // Speicherplatz wie bei der unter b). } // end of namespace N_Aufgabe_4 void __fastcall TForm1::Aufg_4Click(TObject *Sender) { // N_Aufgabe_4::test(); } //------ Aufgabe 5 --------------------------------------------------namespace N_Aufgabe_5 { struct C { C() { Form1->Memo1->Lines->Add("Standardkonstruktor C"); } C(const C& c) { Form1->Memo1->Lines->Add("Copy-Konstruktor C"); } C& operator=(const C&) { Form1->Memo1->Lines->Add("operator= C"); return *this; } ~C() { Form1->Memo1->Lines->Add("Destruktor C"); } }; struct D:C { }; void test() { D d1; // D d2=d1; // d2=d1; // } // //
Standardkonstruktor C Copy-Konstruktor C operator= C Destruktor C Destruktor C
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
341
342
12 Lösungen
} // end of namespace N_Aufgabe_5 void __fastcall TForm1::Aufg_5Click(TObject *Sender) { N_Aufgabe_5::test(); } //------ Aufgabe 6 --------------------------------------------------void __fastcall TForm1::Aufg_6Click(TObject *Sender) { AnsiString s= "Da Konstruktoren nicht vererbt werden, stehen diese in der abgeleiteten " "Klasse nicht zur Verfügung. Bei einer Klasse mit vielen nützlichen " "Konstruktoren wie z.B. einer Stringklasse kann das ein gravierender " "Nachteil sein. Man kann diese zwar in der abgeleiteten Klasse wieder " "alle definieren. Es ist aber fraglich, ob sich dieser Aufwand lohnt und " "man nicht besser beim Aufruf eines vorhandenen Konstruktors eine " "Funktion wie StrToInt oder StrToFloat verwendet."; Memo1->Lines->Add("6."); Memo1->Lines->Add(s); }
//-------------------------------------------------------------------// Aufgaben 8.3.9 //-------------------------------------------------------------------//------ Aufgabe 1 --------------------------------------------------void __fastcall TForm1::Aufg9_1Click(TObject *Sender) { AnsiString a= "Ein Automobil hat einen Motor und Räder. Vermutlich wird nie jemand " "sagen, dass ein Automobil ein Motor oder ein Rad ist."; AnsiString b= "Eine Katze bzw. ein Hund ist Tier. Von einer 'hat ein'-Beziehung kann " "man höchstens dann reden, wenn ein Hund eine Katze gefressen hat oder " "eine Katze einen Hund hält."; AnsiString c= "Ein Landfahrzeug bzw. ein Wasserfahrzeug ist ein Fahrzeug. Ein Automobil " "ist ein Landfahrzeug, ein Segelboot ist ein Wasserfahrzeug. Von einer " "'hat ein'-Beziehung kann man höchstens dann reden, wenn ein " "Wasserfahrzeug ein Landfahrzeug transportiert."; AnsiString d= "Hier sind beide Sichtweisen möglich: Ein Abteilungsleiter und eine " "Sekretärin sind Mitarbeiter. Ein Abteilungsleiter hat eine Sekretärin " "und Mitarbeiter. Im ersten Fall werden die Mitarbeiter einer Firma " "modelliert. Im zweiten Fall wird eine Abteilung modelliert. Es soll " "auch Sekretärinnen geben, die einen Abteilungsleiter haben."; Memo1->Lines->Add("1."); Memo1->Lines->Add("a)"); Memo1->Lines->Add(a); Memo1->Lines->Add("b)"); Memo1->Lines->Add(b); Memo1->Lines->Add("c)"); Memo1->Lines->Add(c); Memo1->Lines->Add("d)"); Memo1->Lines->Add(d); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
} //------ Aufgabe 2 --------------------------------------------------namespace N_Aufgabe_9_2 { // a) class CQuadrat1{ double a; public: CQuadrat1(double a_):a(a_) {} double Flaeche() { return a*a; } double Umfang() { return 4*a; } }; class CRechteck1:public CQuadrat1{ double b; public: CRechteck1(double a_, double b_):CQuadrat1(a_),b(b_) {} }; // b) class CRechteck2{ double a,b; public: CRechteck2(double a_,double b_):a(a_),b(b_){}; double Flaeche() { return a*b; } double Umfang() { return 2*(a+b); } }; class CQuadrat2:public CRechteck2 { public: CQuadrat2(double a_):CRechteck2(a_,a_) {} }; } // end of namespace N_Aufgabe_4 void __fastcall TForm1::Aufg9_2Click(TObject *Sender) { AnsiString s= "Bei der Ableitung eines Quadrats von einem Rechteck sind die aus der " "Basisklasse geerbten Funktionen Flaeche und Umfang auch in der " "abgeleiteten Klasse korrekt. Bei der umgekehrten Ableitung ist das " "nicht richtig."; Memo1->Lines->Add("2."); Memo1->Lines->Add(s); } //------ Aufgabe 3 --------------------------------------------------void __fastcall TForm1::Aufg9_3Click(TObject *Sender) { AnsiString a= "Ein Grundstück, eine Eigentumswohnung, ein Schloss und ein Gewerbeobjekt " "sind eine Immobilie. Ein Einfamilienhaus ist dagegen keine " "Eigentumswohnung. "; Memo1->Lines->Add("3."); Memo1->Lines->Add("a)"); Memo1->Lines->Add(a); AnsiString b= "Durch eine Klasse wie CWohnimmobilie, von der eine Eigentumswohnung " "und ein Einfamilienhaus abgeleitet werden. "; Memo1->Lines->Add("b)"); Memo1->Lines->Add(b); } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
343
344
12 Lösungen
//------ Aufgabe 4 --------------------------------------------------void __fastcall TForm1::Aufg9_4Click(TObject *Sender) { AnsiString s1= "Bei der Funktion Abstand stellt sich die Frage, was der Abstand eines " "Quadrats vom Ursprung überhaupt sein soll: Soll vom Mittelpunkt, der " "linken unteren Ecke oder einem anderen Punkt gemessen werden?"; AnsiString s2= "Bei der Funktion drehe erwartet man normalerweise, dass die Seiten des " "Quadrats nach einer Drehung um z.B. 70 Grad nicht mehr parallel zu den " "Koordinatenachsen sind. "; Memo1->Lines->Add("4."); Memo1->Lines->Add(s1); Memo1->Lines->Add(s2); } //------ Aufgabe 5 --------------------------------------------------void __fastcall TForm1::Aufg9_5Click(TObject *Sender) { AnsiString s= "Mit einer Konversion der Basisklasse in eine abgeleitete Klasse wäre der " "Aufruf einer Funktion der Basisklasse über ein Objekt der abgeleiteten " "Klasse möglich. Eine solche Funktion muss aber überhaupt nicht " "existieren. "; Memo1->Lines->Add("5."); Memo1->Lines->Add(s); }
//-------------------------------------------------------------------// Aufgaben 8.3.11 //-------------------------------------------------------------------#include <math.h> namespace N_Aufg_8_3_11 { //------ Aufgabe 1 --------------------------------------------------class CKreis{ double r; static const double pi; public: CKreis(double Radius) { r=Radius; } double Flaeche() { return r*r*pi; } double Umfang() { return 2*r*pi; } AnsiString toStr() { return "Kreis mit Radius "+FloatToStr(r); } }; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
const double CKreis::pi=3.14; class C2DPunkt { double x,y; public: C2DPunkt(double x_,double y_):x(x_),y(y_) { } double Abstand() { return sqrt(x*x+y*y); } AnsiString toStr() { return FloatToStr(x)+"|"+FloatToStr(y); } }; // a) namespace N_a { class C2DKreis:public CKreis, public C2DPunkt { public: C2DKreis(double Radius=1, double x=0, double y=0): CKreis(Radius), C2DPunkt(x,y) { } C2DKreis(double Radius, C2DPunkt pos): CKreis(Radius), C2DPunkt(pos) { } /* Eine in dieser Klasse definierte Funktion toStr verdeckt die Funktionen toStr der Basisklasse: AnsiString toStr() { return CKreis::toStr()+" Pos: "+C2DPunkt::toStr(); } */ }; AnsiString s1= "C2DKreis erbt alle public Elementfunktionen der Basisklassen, also " "Flaeche, Umfang und toStr. Ein Aufruf von toStr über ein Objekt der " "Klasse C2DKreis ist dann mehrdeutig."; AnsiString s2= "Diese Hierarchie ist nicht geeignet, da ein Kreis mit einer Position " "zwar ein Kreis, aber kein Punkt ist."; } // end of namespace N_a // b) namespace N_b { class C2DKreis { CKreis Kreis; C2DPunkt Position; public: C2DKreis(double Radius=1, double x=0, double y=0): Kreis(Radius), Position(x,y) { } C2DKreis(double Radius, C2DPunkt pos): Kreis(Radius), Position(pos) { } // Eine in dieser Klasse definierte Funktion toStr verdeckt keine // Funktionen der Basisklasse: Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
345
346
12 Lösungen
AnsiString toStr() { return Kreis.toStr()+" Pos: "+Position.toStr(); } }; AnsiString s1= "Da hier keine Vererbung verwendet wird, erbt C2DKreis keine " "Elementfunktionen der Klasse CKreis und kann die Funktionen dieser " "Klasse CKreis auch nicht wiederverwenden."; AnsiString s2= "Diese Hierarchie ist nicht geeignet, da ein Kreis mit einer Position ein " "Kreis ist, aber die Funktionen der Klasse CKreis nicht verwendet."; } // end of namespace N_b // c) namespace N_c { class C2DKreis:public CKreis { C2DPunkt Position; public: C2DKreis(double Radius=1, double x=0, double y=0): CKreis(Radius), Position(x,y) { } C2DKreis(double Radius, C2DPunkt pos): CKreis(Radius), Position(pos) { } }; AnsiString s1= "C2DKreis erbt alle public Elementfunktionen der Basisklasse, also " "Flaeche, Umfang und toStr. Alle diese Funktionen liefern auch über " "ein Objekt der Klasse C2DKreis richtige Ergebnisse. Die " "Elementfunktionen von C2DPunkt stehen dagegen nicht zur Verfügung."; AnsiString s2= "Diese Hierarchie ist geeignet, da ein C2DKreis ein Kreis, aber kein " "C2DPunkt ist."; } // end of namespace N_c // d) namespace N_d { class C2DKreis{// Lösung von Aufgabe 8.2.2.2 double r; C2DPunkt Position; public: C2DKreis(double Radius=1, double x=0, double y=0): r(Radius), Position(x,y) { } // Reihenfolge der Deklaration C2DKreis(double Radius, C2DPunkt pos): r(Radius), Position(pos) { } // Reihenfolge der Deklaration AnsiString toStr() { return "Kreis mit Radius "+FloatToStr(r)+" in Position "+Position.toStr(); } }; AnsiString s1= "Wenn man in der Klasse C2DKreis die Funktionen Flaeche, Abstand usw. " "für einen Kreis haben will, muss man sie erneut definieren. Diese " Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
347
"Redundanz sollte man vermeiden."; AnsiString s2= "Abgesehen davon ist diese Alternative geeignet."; } // end of namespace N_d void test() { // a) Form1->Memo1->Lines->Add("a)"); N_a::C2DKreis ka(1,2,3); Form1->Memo1->Lines->Add(FloatToStr(ka.Umfang())); Form1->Memo1->Lines->Add(FloatToStr(ka.Abstand())); Form1->Memo1->Lines->Add(FloatToStr(ka.Flaeche())); //Form1->Memo1->Lines->Add(ka.toStr()); // Fehler:
// das geht // das geht // das geht Mehrdeutig
Form1->Memo1->Lines->Add(N_a::s1); Form1->Memo1->Lines->Add(""); Form1->Memo1->Lines->Add(N_a::s2); // b) Form1->Memo1->Lines->Add("b)"); N_b::C2DKreis kb(1,2,3); // Form1->Memo1->Lines->Add(FloatToStr(kb.Umfang())); // Form1->Memo1->Lines->Add(FloatToStr(kb.Abstand())); // Form1->Memo1->Lines->Add(FloatToStr(kb.Flaeche())); Form1->Memo1->Lines->Add(kb.toStr());
// // // //
geht nicht geht nicht geht nicht das geht
// // // //
das geht geht nicht das geht das geht
Form1->Memo1->Lines->Add(N_b::s1); Form1->Memo1->Lines->Add(""); Form1->Memo1->Lines->Add(N_b::s2); // c) Form1->Memo1->Lines->Add("c)"); N_c::C2DKreis kc(1,2,3); Form1->Memo1->Lines->Add(FloatToStr(kc.Umfang())); // Form1->Memo1->Lines->Add(FloatToStr(kc.Abstand())); Form1->Memo1->Lines->Add(FloatToStr(kc.Flaeche())); Form1->Memo1->Lines->Add(kc.toStr()); Form1->Memo1->Lines->Add(N_c::s1); Form1->Memo1->Lines->Add(""); Form1->Memo1->Lines->Add(N_c::s2); // d) Form1->Memo1->Lines->Add("d)"); N_d::C2DKreis kd(1,2,3); // Form1->Memo1->Lines->Add(FloatToStr(kd.Umfang())); // geht nicht // Form1->Memo1->Lines->Add(FloatToStr(kd.Abstand())); // geht nicht Form1->Memo1->Lines->Add(N_d::s1); Form1->Memo1->Lines->Add(""); Form1->Memo1->Lines->Add(N_d::s2); } } // end of namespace N_Aufg_8_3_11 void __fastcall TForm1::Aufg11_1Click(TObject *Sender) { N_Aufg_8_3_11::test(); } //------ Aufgabe 2 --------------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
348
namespace N_Aufg_8_3_11 { class C { public: C() { Form1->Memo1->Lines->Add("C"); } ~C() { Form1->Memo1->Lines->Add("~C"); } int a; }; class D3:virtual public C { public: D3() { Form1->Memo1->Lines->Add("D3"); } ~D3() { Form1->Memo1->Lines->Add("~D3"); } }; class D4: public virtual C { public: D4() { Form1->Memo1->Lines->Add("D4"); } ~D4() { Form1->Memo1->Lines->Add("~D4"); } }; class D5: public virtual C { public: D5() { Form1->Memo1->Lines->Add("D5"); } ~D5() { Form1->Memo1->Lines->Add("~D5"); } }; class E:public D3, public D4, public D5 { public: E() { Form1->Memo1->Lines->Add("E"); } ~E() { Form1->Memo1->Lines->Add("~E"); } }; } // end of namespace N_Aufg_8_3_11 void __fastcall TForm1::Aufg11_2Click(TObject *Sender) { N_Aufg_8_3_11::E e; // C // D3 // D4 // D5 // E } // ~E // ~D5 // ~D4 // ~D3 // ~C
12.7.9 Aufgaben 8.4
// Datei: d:\Loesungen\kap-8\8.4\VirtFktU.cpp
#include "VirtFktU.h" //----- Aufgabe 8.4.3.1 ---------------------------------------------class C1DPunkt { protected: // private wäre besser double x; public: Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
C1DPunkt(double x_):x(x_)
349
{
}
virtual AnsiString toStr() { return FloatToStr(x); } void display() { Form1->Memo1->Lines->Add(toStr()); } // b) virtual double length(); }; class C2DPunkt:public C1DPunkt { protected: // private wäre besser double y; public: C2DPunkt(double x_,double y_):C1DPunkt(x_),y(y_)
{
}
AnsiString toStr() { return FloatToStr(x)+"|"+FloatToStr(y); } // b) double length(); }; class C3DPunkt : public C2DPunkt { double z; public: C3DPunkt(double x_,double y_,double z_):C2DPunkt(x_,y_),z(z_)
{
}
AnsiString toStr() { return FloatToStr(x)+"|"+FloatToStr(y)+"|"+FloatToStr(z); } // b) double length(); }; void test_Punkte() { C1DPunkt p1(1); C2DPunkt p2(1,2); C3DPunkt p3(1,2,3); p1.display(); // C1DPunkt::toStr p2.display(); // C2DPunkt::toStr p3.display(); // C3DPunkt::toStr } // b) #include <math.h> double C1DPunkt::length() { return fabs(x); }; double C2DPunkt::length() { return sqrt(x*x + y*y); }; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
350
12 Lösungen
double C3DPunkt::length() { return sqrt(x*x + y*y + z*z); }; void show(C1DPunkt& p) { Form1->Memo1->Lines->Add(FloatToStr(p.length())); }; void test_show() { C1DPunkt p1(1); C2DPunkt p2(1,2); C3DPunkt p3(1,2,3); show(p1); // C1DPunkt::toStr show(p2); // C2DPunkt::toStr show(p3); // C3DPunkt::toStr } void __fastcall TForm1::Aufg_8_4_3_1Click(TObject *Sender) { test_Punkte(); test_show(); } //----- Aufgabe 8.4.3.2 ---------------------------------------------void __fastcall TForm1::Aufg_8_4_3_2Click(TObject *Sender) { AnsiString s= "Diese Funktionen können nicht virtuell definiert werden, da alle ihre " "Parameterlisten verschieden sind."; Memo1->Lines->Add(s); } //-----
Aufgabe 8.4.3.3 ---------------------------------------------
// b) Die Funktionsdefinitionen sind in ImmoUnit.cpp. #include "immounit.h" // a) Eine einzige Definition der Funktion display reicht aus: void display(const CImmobilie* i, TMemo* Memo) { Memo->Lines->Add(i->toStr()); } // Die übrigen Klassen werden dann von der Definition in der // Header-Datei abgeleitet: class CGrundstueck : public CImmobilie { double Flaeche; public: CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_): CImmobilie(Anschrift_,Kaufpreis_),Flaeche(Flaeche_) { } AnsiString toStr() const { return "Grundstück"+CImmobilie::toStr()+ " Fläche: "+FloatToStr(Flaeche)+ " qm"; } }; class CWohnImmobilie : public CImmobilie { protected: // Nur zur Vereinfachung - private wäre besser double Wohnflaeche; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
351
int AnzahlZimmer; public: CWohnImmobilie(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, int AnzahlZimmer_): CImmobilie(Anschrift_,Kaufpreis_), Wohnflaeche (Wohnflaeche_), AnzahlZimmer(AnzahlZimmer_){} AnsiString toStr() const { return CImmobilie::toStr()+" Wohnfläche: "+ FloatToStr(Wohnflaeche)+" qm Zimmer: "+IntToStr(AnzahlZimmer); } }; class CEigentumswohnung : public CWohnImmobilie { public: CEigentumswohnung(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, int AnzahlZimmer_): CWohnImmobilie(Anschrift_, Kaufpreis_, Wohnflaeche_, AnzahlZimmer_){} AnsiString toStr() const { return "ETW"+CWohnImmobilie::toStr(); } }; class CEinfamilienhaus : public CWohnImmobilie { double Grundstuecksgroesse; public: CEinfamilienhaus(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, double Grundstuecksgroesse_, int AnzahlZimmer_): CWohnImmobilie(Anschrift_, Kaufpreis_, Wohnflaeche_, AnzahlZimmer_), Grundstuecksgroesse(Grundstuecksgroesse_){} AnsiString toStr() const { return "EFH"+CWohnImmobilie::toStr(); } }; void test_Imm() { // CImmobilie(char* Anschrift_, double Kaufpreis_):Kaufpreis(Kaufpreis_) CImmobilie imm("", 100); display (&imm, Form1->Memo1); // CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_) CGrundstueck g("Tübingen",100000,400); display(&g, Form1->Memo1); // CEigentumswohnung(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, int AnzahlZimmer_) CEigentumswohnung etw("Stuttgart",500000,120,5); display(&etw, Form1->Memo1); // CEinfamilienhaus(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_, double Grundstuecksgroesse_, int AnzahlZimmer_) CEinfamilienhaus efh("Kiel",300000,150,900,5); display(&efh, Form1->Memo1); } void __fastcall TForm1::Aufg_8_4_3_3Click(TObject *Sender) { test_Imm(); } //----- Aufgabe 8.4.3.4 ---------------------------------------------struct C { virtual int f(int i=1) };
{ return i; }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
352
struct D:public C { virtual int f(int i=2) };
12 Lösungen
{ return i; }
void test() { C* pc=new D; int i=pc->f(); // pc->f(1); C* pd=new D; int j=pd->f(); // pd->f(1); // Das Default-Argument wird immer nach dem statischen Datentyp bestimmt Form1->Memo1->Lines->Add(i); Form1->Memo1->Lines->Add(j); }; void __fastcall TForm1::Aufg_8_4_3_4Click(TObject *Sender) { test(); } //----- Aufgabe 8.4.3.5 ---------------------------------------------void __fastcall TForm1::Aufg_8_4_3_5Click(TObject *Sender) { /* Wenn die Funktion D::f virtuell wäre, würde mit C c; D d; der Aufruf g(d,c); zum Aufruf der Funktion D::f(c) führen. Diese Funktion würde auf c.e zugreifen, das in C aber gar nicht existiert. (siehe Mössenbeck, 1992, S. 75.) */ }
//----- Aufgabe 8.4.10 ----------------------------------------------//----- Aufgabe 8.4.10.1 --------------------------------------------namespace N_Aufgabe_1 { class CFigur { public: virtual double Flaeche()=0; virtual double Umfang()=0; virtual AnsiString toStr()=0; ~CFigur() {}; }; class CRechteck : public CFigur { double a,b; public: CRechteck(double a_,double b_): a(a_), b(b_) {} double Flaeche() { return a*b; } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
double Umfang() { return 2*(a+b); } AnsiString toStr() { return "Rechteck mit a="+FloatToStr(a)+" und b="+FloatToStr(b); } }; class CQuadrat : public CFigur { double a; public: CQuadrat(double a_):a(a_) { } double Flaeche() { return a*a; } double Umfang() { return 4*a; } AnsiString toStr() { return "Quadrat mit a="+FloatToStr(a); } }; void test() { CQuadrat q=CQuadrat(1); CRechteck r=CRechteck(2,3); CFigur* a[2]={&r , &q }; for (int i=0; i<2; i++) { Form1->Memo1->Lines->Add(a[i]->toStr()); Form1->Memo1->Lines->Add("Fläche: "+FloatToStr(a[i]->Flaeche())+" Umfang: "+FloatToStr(a[i]->Umfang())); } } } // end of namespaace N_Aufgabe_1 void __fastcall TForm1::Aufg8_4_10_1Click(TObject *Sender) { N_Aufgabe_1::test(); } //----- Aufgabe 8.4.10.2 --------------------------------------------#include #include using namespace std; namespace N_Aufg8_4_10_2 { class C2DPunkt{ public: // Zur Vereinfachung, spart Zugriffsfunktionen double x,y; public: C2DPunkt(double x_=0, double y_=0):x(x_),y(y_) { }; // C2DPunkt (ifstream& f) { // erzeugt einen C2DPunkt mit den Daten aus dem ifstream f>>*this; } AnsiString toStr() const { return "("+FloatToStr(x)+"|"+FloatToStr(y)+")"; } friend ostream& operator<<(ostream& f, const C2DPunkt& p); friend istream& operator>>(istream& f, C2DPunkt& p); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
353
354
void verschiebe_um(const C2DPunkt& pos) { x=x+pos.x; y=y+pos.y; }; }; ostream& operator<<(ostream& f, const C2DPunkt& p) { return f<<'('<>(istream& f, C2DPunkt& p) { char k1,sep,k2; f>>k1>>p.x>>sep>>p.y>>k2; // Einlesen der Elemente von T return f; } class CFigur { public: // a) virtual void zeichne() const =0; virtual AnsiString toStr()=0; virtual ~CFigur(){}; // den virtuellen Destruktor nicht vergessen // c) virtual void write(ofstream& f)=0; // 8.4.11.1 a) void loesche() const { TColor StiftFarbe=Form1->Image1->Canvas->Pen->Color; Form1->Image1->Canvas->Pen->Color=clWhite; // Hintergrundfarbe zeichne(); Form1->Image1->Canvas->Pen->Color=StiftFarbe; } virtual void verschiebe_Position_um(const C2DPunkt& deltaPos)=0; // 8.4.11.1 b) virtual void verschiebe_um(const C2DPunkt& deltaPos) { loesche(); verschiebe_Position_um(deltaPos); zeichne(); } }; // b) class CZeichnung { typedef vector Container; typedef vector::iterator Iterator; Container c; public: CZeichnung(){ }; void einfuegen(CFigur* p) { c.push_back(p); } // a) void zeichne() { for (Iterator i=c.begin(); i!=c.end(); i++) (*i)->zeichne(); } Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
// c) void write(char* Dateiname) { ofstream f(Dateiname); for (Iterator i=c.begin(); i!=c.end(); i++) (*i)->write(f); f.close(); // überflüssig, aber nicht falsch } // d) void LeseZeichnung(char* Dateiname); void toMemo(TMemo* m) { for (Iterator i=c.begin(); i!=c.end(); i++) m->Lines->Add((*i)->toStr().c_str()); } void loesche() { for (Iterator i=c.begin(); i!=c.end(); i++) (*i)->loesche(); } void verschiebe_um(C2DPunkt deltaPos) { for (Iterator i=c.begin(); i!=c.end(); i++) (*i)->verschiebe_um(deltaPos); } static const char Stringterminator; }; const char CZeichnung::Stringterminator='\0'; class CGerade:public CFigur { C2DPunkt ap,ep; // Anfangs- und Endpunkt public: CGerade(C2DPunkt ap_, C2DPunkt ep_):ap(ap_),ep(ep_) {} void zeichne() const { Form1->Image1->Canvas->MoveTo(ap.x,ap.y); Form1->Image1->Canvas->LineTo(ep.x,ep.y); }; void verschiebe_Position_um(const C2DPunkt& deltaPos) { ap.verschiebe_um(deltaPos); ep.verschiebe_um(deltaPos); } void write(ofstream& f) { f<
355
356
const char* CGerade::class_id="Gerade"; class CKreis:public CFigur { C2DPunkt M; // Mittelpunkt double r; // Radius public: CKreis(C2DPunkt M_, double r_):M(M_),r(r_) {} // a) void zeichne() const { Form1->Image1->Canvas->Ellipse(M.x-r,M.y-r,M.x+r,M.y+r); }; AnsiString toStr() { return "Kreis MP="+M.toStr()+" Radius="+FloatToStr(r); } // c) void write(ofstream& f) { f<>M>>r>>Separator; } static const char* class_id; void verschiebe_Position_um(const C2DPunkt& deltaPos) { M.verschiebe_um(deltaPos); } }; const char* CKreis::class_id="Kreis"; class CRechteck:public CFigur { C2DPunkt lu,ro; // Eckpunkte links unten und rechts oben public: CRechteck(C2DPunkt lu_, C2DPunkt ro_):lu(lu_),ro(ro_) {} // a) void zeichne() const { Form1->Image1->Canvas->Rectangle(lu.x,lu.y,ro.x,ro.y); // zeichnet ein gefülltes Rechteck, das den // Hintergrund verdeckt }; AnsiString toStr() { return "Rechteck links unten="+lu.toStr()+" rechts oben="+ro.toStr(); } // c) void write(ofstream& f) { f<
12 Lösungen
12.7 Lösungen Kapitel 8
357
{ } // erzeugt ein Rechteck mit den Daten aus dem ifstream void verschiebe_Position_um(const C2DPunkt& deltaPos) { lu.verschiebe_um(deltaPos); ro.verschiebe_um(deltaPos); } static const char* class_id; }; const char* CRechteck::class_id="Rechteck"; void CZeichnung::LeseZeichnung(char* Dateiname) {// Diese Funktion muss bei einer Erweiterung um neue Figuren angepasst werden. ifstream f(Dateiname); string s; while (getline(f,s,Stringterminator)) // Lese nullterminierten String { CFigur* pf; if (s==CGerade::class_id) pf=new CGerade(f); else if (s==CKreis::class_id) pf=new CKreis(f); else if (s==CRechteck::class_id) pf=new CRechteck(f); else ShowMessage("unexpected data: "+AnsiString(s.c_str())); c.push_back(pf); } f.close(); // überflüssig, aber nicht falsch } void test_ReadWrite(char* Dateiname) { // testet das Lesen und Schreiben einer Zeichnung. // Erzeuge eine Zeichnung: CZeichnung z; for (int i=0; i<100; i++) if (i%3==0) z.einfuegen(new CGerade(C2DPunkt(i,i+1),C2DPunkt(i+2,i+3))); else if (i%3==1) z.einfuegen(new CKreis(C2DPunkt(i,i+1),i+2)); else if (i%3==2) z.einfuegen(new CRechteck(C2DPunkt(i,i+1),C2DPunkt(i+2,i+3))); // Schreibe die Zeichnung in eine Datei: z.write(Dateiname); // Lese die gespeicherten Daten: z.LeseZeichnung(Dateiname); // Zeige die Daten an: z.toMemo(Form1->Memo1); } // b) CZeichnung einfache_Zeichnung() { CZeichnung z; z.einfuegen(new CRechteck(C2DPunkt(40,40),C2DPunkt(100,100))); z.einfuegen(new CGerade(C2DPunkt(40,40),C2DPunkt(70,10))); z.einfuegen(new CGerade(C2DPunkt(70,10),C2DPunkt(100,40))); z.einfuegen(new CKreis(C2DPunkt(70,40),5)); return z; } CZeichnung z=einfache_Zeichnung(); } // end of namespace N_Aufg8_4_10_2 void __fastcall TForm1::Aufg8_4_10_2Click(TObject *Sender) Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
358
12 Lösungen
{ using namespace N_Aufg8_4_10_2; // test_ReadWrite("Zeichnung.drw"); // return; static int i=0; if (i<3) { z.verschiebe_um(N_Aufg8_4_10_2::C2DPunkt(10,10)); z.zeichne(); } if (i==3) z.loesche(); if (i<4) i++; else i=0; } //----- Aufgabe 8.4.10.3 --------------------------------------------void __fastcall TForm1::Aufg8_4_10_3Click(TObject *Sender) { AnsiString s= "Bei dieser wie auch bei den meisten anderen Klassen kann man " "nicht ausschliessen, dass sie so verwendet werden, dass ein " "virtueller Destruktor notwendig ist. Da ein unnötiger virtueller " "Destruktor nicht mit bemerkenswerten Nachteilen verbunden ist, " "sollte man jede dieser Klassen mit einem virtuellen Destruktor definieren."; Form1->Memo1->Lines->Add(s); } //----- Aufgabe 8.4.11 ----------------------------------------------//----- Aufgabe 8.4.11.1 --------------------------------------------void __fastcall TForm1::Aufg8_4_11_1Click(TObject *Sender) { AnsiString s= "Die Lösung diese Aufgabe ist in der Lösung der Aufgabe 8.4.10 enthalten. " "Die Funktionen sind in der Basisklasse CFigur definiert und werden " "in den abgeleiteten Klassen überschrieben."; Memo1->Lines->Add(s); } namespace N_8_4_11 { //----- Aufgabe 8.4.11.2 --------------------------------------------// Dieses Design soll nur eine Skizze sein class Uebertragung { // abstrakte Basisklasse public: virtual void VerbindungAufbauen(AnsiString Ziel)=0; virtual void DatenUebertragen()=0; virtual void VerbindungBeenden()=0; }; class UMTS_Uebertragung : public Uebertragung { virtual void VerbindungAufbauen(AnsiString Ziel) { // spezifische Anweisungen für UMTS }; virtual void DatenUebertragen() { // spezifische Anweisungen für UMTS }; virtual void VerbindungBeenden() Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
359
{ // spezifische Anweisungen für UMTS }; }; class Modem_Uebertragung : public Uebertragung
{
virtual void VerbindungAufbauen(AnsiString Ziel) { // spezifische Anweisungen für Modems }; virtual void DatenUebertragen() { // spezifische Anweisungen für Modems }; virtual void VerbindungBeenden() { // spezifische Anweisungen für Modems }; }; class ISDN_Uebertragung : public Uebertragung
{
virtual void VerbindungAufbauen(AnsiString Ziel) { // spezifische Anweisungen für ISDN }; virtual void DatenUebertragen() { // spezifische Anweisungen für ISDN }; virtual void VerbindungBeenden() { // spezifische Anweisungen für ISDN }; }; void SendeDaten(Uebertragung& u,AnsiString Ziel) { u.VerbindungAufbauen(Ziel); u.DatenUebertragen(); u.VerbindungBeenden(); }; } // end of namespace N_8_4_11 void __fastcall TForm1::Aufg8_4_11_2Click(TObject *Sender) { using namespace N_8_4_11; ISDN_Uebertragung i; Modem_Uebertragung m; UMTS_Uebertragung u; SendeDaten(u,"Ziel"); } //----- Aufgabe 8.4.12 ----------------------------------------------namespace N_Gamma_Abstract_Windows_Factory { void display (AnsiString s) { Form1->Memo1->Lines->Add(s); } class Button { // abstract product public: // definiert die Schnittstelle für das Produkt Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
360
12 Lösungen
virtual void OnClick () {}; // Gemeinsame Schnittsteller aller Buttons }; class MSWindowsButton:public Button { // concrete product public: // definiert, wie ein Produkt konstruiert wird, // und implementiert seine Schnittstelle MSWindowsButton() { display("MSWindowsButton erzeugt"); }; void OnClick () { display("MS click"); }; }; class XWindowsButton:public Button { // concrete product public: XWindowsButton() { display("XWindowsButton erzeugt"); }; void OnClick () { display("X click"); }; }; class Scrollbar { // abstract product // Gemeinsame Schnittsteller aller Scrollbars }; class MSWindowsScrollbar:public Scrollbar { public: // definiert, wie ein Produkt konstruiert wird, // und implementiert seine Schnittstelle MSWindowsScrollbar() { display("MSWindowsScrollbar erzeugt"); }; }; class XWindowsScrollbar:public Scrollbar { public: // definiert, wie ein Produkt konstruiert wird, // und implementiert seine Schnittstelle XWindowsScrollbar() { display("XWindowsScrollbar erzeugt"); };
};
class GUIFactory { // abstract factory public: // Schnittstelle für Operationen, die abstrakte Produkte konstruieren virtual Button* CreateButton()=0; virtual Scrollbar* CreateScrollbar()=0; }; class MSWindowsFactory: public GUIFactory { // concrete factory public: // implementiert die Konstruktion konkreter Produkte Button* CreateButton() {return new MSWindowsButton; } Scrollbar* CreateScrollbar() {return new MSWindowsScrollbar; } }; class XWindowsFactory: public GUIFactory { // concrete factory public: // implementiert die Konstruktion konkreter Produkte Button* CreateButton() { return new XWindowsButton; } Scrollbar* CreateScrollbar() { return new XWindowsScrollbar; } }; void CreateAppWindow(GUIFactory* guiFactory) { // verwendet nur abstract factory und abstrakte Produkte Button* b=guiFactory->CreateButton(); b->OnClick(); Scrollbar* s=guiFactory->CreateScrollbar(); } void test() { CreateAppWindow(new MSWindowsFactory); CreateAppWindow(new XWindowsFactory); // GUIFactory* guiFactory=new MSWindowsFactory(); // guiFactory=new XWindowsFactory(); } } // end of namespace N_Gamma_Abstract_Windows_Factory Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
361
void __fastcall TForm1::Aufg8_4_12Click(TObject *Sender) { N_Gamma_Abstract_Windows_Factory ::test(); } //----- Aufgabe 8.4.14 ----------------------------------------------void display(AnsiString s) { Form1->Memo1->Lines->Add(s); } class CBasisklasse{ public: virtual void anzeigen()=0; virtual void drucken()=0; virtual void aendern()=0; }; class CBestellung public: void anzeigen() { display("Best }; void drucken() { display("Best }; void aendern() { display("Best }; };
: public CBasisklasse{
anzeigen");
drucken");
aendern");
class CLagerbestand : public CBasisklasse{ public: void anzeigen() { display("LB anzeigen"); }; void drucken() { display("LB drucken"); }; void aendern() { display("LB aendern"); }; }; enum Aktion {anzeigen,drucken,aendern}; void dispatch1(Aktion a, CBasisklasse* p) { if (a==anzeigen) p->anzeigen(); else if (a==drucken) p->drucken(); else if (a==aendern) p->aendern(); } void dispatch(Aktion a, CBasisklasse* p) { typedef void (CBasisklasse::* ZEFB)(); ZEFB Aktionen[]={&CBasisklasse::anzeigen, &CBasisklasse::drucken, &CBasisklasse::aendern}; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
362
12 Lösungen
(p->*Aktionen[a])(); } void __fastcall TForm1::ZeigerEltFktClick(TObject *Sender) { CBasisklasse* p; CBestellung best; p=&best; dispatch(anzeigen,p); dispatch(drucken,p); dispatch(aendern,p); CLagerbestand lag; p=&lag; dispatch(anzeigen,p); dispatch(drucken,p); dispatch(aendern,p); }
12.7.10 Aufgabe 8.4.3.3
// Datei: d:\Loesungen\kap-8\8.4\immounit.h
#ifndef ImmoUnitH #define ImmoUnitH class CImmobilie { protected: // Nur zur Vereinfachung - private wäre besser AnsiString Anschrift; double Kaufpreis; public: CImmobilie(char* Anschrift_, double Kaufpreis_); virtual ~CImmobilie(); // virtuellen Destruktor nicht vergessen virtual AnsiString toStr() const; }; #endif // Datei: d:\Loesungen\kap-8\8.4\immounit.cpp
#include "ImmoUnit.h" CImmobilie::CImmobilie(char* Anschrift_, double Kaufpreis_):Anschrift(Anschrift_),Kaufpreis(Kaufpreis_) { } CImmobilie::~CImmobilie() { } AnsiString CImmobilie::toStr() const { return " in "+AnsiString(Anschrift)+ " KP: "+FloatToStr(Kaufpreis) + " DM "; }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
363
12.7.11 Aufgabe 8.5
// Datei: d:\Loesungen\kap-8\8.5\RTTIU.cpp
#include "RTTIU.h" //---------- Aufgabe 1 ----------------------------------------------#include using namespace std; void Aufgabe_1() { class C { virtual // für Aufgabe b) auskommentieren void f(){}; // damit C1 polymorph ist } c; class D1:public C { } d1; class D2:public C { } d2; // a) c=d1; bool b1= typeid(c)==typeid(d1); c=d2; bool b2= typeid(c)==typeid(d2);
// false
// false
// false
// false
D1* pd1=&d1; D2* pd2=&d2; C* pc=pd1; bool b3= typeid(*pc)==typeid(d1); // true bool b4= typeid(pc)==typeid(pd1); // false pc=&c; bool b5= typeid(*pc)==typeid(d1); // false C& rc=c; bool b6= typeid(rc)==typeid(d1); bool b7= typeid(c)==typeid(d1); rc=d1; bool b8= typeid(rc)==typeid(d1); C& rc1=d1; bool b9= typeid(rc1)==typeid(d1);
b)
// false // false // false
// false // false
// false // false
// false
// false
// true
// false
int Breakpoint=1; { // c) class E1:public D2 { } e1; class E2:public D2 { } e2; // /* für Aufgabe b) auskommentieren D1* pd1=&d1; D2* pd2=&d2; E1* pe1=&e1; E2* pe2=&e2; C* pc=&c; D2* p1= dynamic_cast(pc); // p1=0 pc=pd1; D2* p2= dynamic_cast(pc); // p2=0 Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
364
pc=pe1; D2* p3= dynamic_cast(pc); // p2!=0 pc=pe2; // Fehler bei private Ableitung D2* p4= dynamic_cast(pc); // p2!=0 // d) Welche Ergebnisse würde man in Aufgabe c) erhalten, // wenn E2 private und nicht public abgeleitete wäre ? C& rc=c; C& rd1=d1; C& rd2=d2; C& re1=e1; C& re2=e2; // D2 x1= dynamic_cast(c); // Exception bad_cast // D2 x2= dynamic_cast(rd1); // Exception bad_cast D2 x3= dynamic_cast(rd2); // keine Exception D2 x4= dynamic_cast(re1); // keine Exception D2 x5= dynamic_cast(re2); // keine Exception int Breakpoint=0; // */ Ende für Aufgabe b) auskommentieren } } void __fastcall TForm1::Aufg_1Click(TObject *Sender) { Aufgabe_1(); } //---------- Aufgabe 2 ----------------------------------------------class C { public: virtual void f() { Form1->Memo1->Lines->Add(typeid(*this).name()); }; C() { f(); } ~C() { f(); } }; class D:public C { public: D() { f(); } ~D() { f(); } }; void __fastcall TForm1::Aufg_2Click(TObject *Sender) { D d; // Lösung: C D D C }
12.8 Lösungen Kapitel 9
12.8.1 Aufgabe 9.2
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.8 Lösungen Kapitel 9 // Datei: d:\Loesungen\kap-9\9.2\PropU.cpp
#include "PropU.h" //-------------------------------------------------------------------class C { int fx; void setx(int x_){fx=x_*x_;}; int getx(){return fx;}; public: __property int x = {read=getx, write=setx}; }; void __fastcall TForm1::Aufg_2_2Click(TObject *Sender) { C p; p.x=17; int y=p.x; Memo1->Lines->Add(y); } //--------------------------------------------------------------------
12.8.2 Aufgabe 9.3
// Datei: d:\Loesungen\kap-9\9.3\ResizeUnit.h
#ifndef ResizeUnitH #define ResizeUnitH class TResize{ int fsw,fsh, faw,fah; bool initialized, PositionAndSize; // true: adjust position and size // false: adjust only position int n_Controls; // müßte meist reichen TForm* Form; struct TWinCoord { int Top, Left, Width, Height; }; TWinCoord* sc; int xt(int x) { // transformiert x-Koordinaten return x*faw/fsw; }; int yt(int y) { // transformiert y-Koordinaten return y*fah/fsh; }; // Da diese Klasse Zeiger enthält, führt der vom Compiler erzeugte // Copy-Konstruktor zu flachen Kopien. Da der aber nie benötigt wird, Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
365
366
// wird sein Aufruf durch eine private Deklaration unterbunden. TResize(TResize&); // Copy-Konstruktor private TResize& operator=(TResize&); void Init(TForm* Form_); public: void Resize(TForm* Form_); TResize(bool PositionAndSize_=false):initialized(false), PositionAndSize(PositionAndSize_) { }; virtual ~TResize() {delete[] sc;} }; #endif // Datei: d:\Loesungen\kap-9\9.3\ResizeUnit.cpp
#include // Mit Datei|Neu Unit erzeugen #pragma hdrstop // #include "ResizeUnit.h"
void TResize::Init(TForm* F) { Form=F; sc=new TWinCoord[F->ComponentCount]; fsw = Form->Width; // Breite des Formulars beim Start fsh = Form->Height; for (int i=0; iComponentCount;i++ ) { // Größe der Komponenten beim Start sc[i].Top = ((TControl*)Form->Components[i])->Top; sc[i].Left = ((TControl*)Form->Components[i])->Left; // Die Konversion ist auch mit dynamic_cast möglich: sc[i].Height = dynamic_cast(Form->Components[i])->Height; sc[i].Width = dynamic_cast(Form->Components[i])->Width; } initialized = true; // Wird bei Create automatisch auf }; // false gesetzt void TResize::Resize(TForm* F) { if (!initialized) Init(F); faw = Form->Width; // Breite des aktuellen Formulars fah = Form->Height; for (int i=0; iComponentCount; i++) if (PositionAndSize) // Position und Größe anpassen ((TWinControl*)Form->Components[i])->SetBounds(xt(sc[i].Left), yt(sc[i].Top),xt(sc[i].Width), yt(sc[i].Height)); else // nur die Position anpassen ((TWinControl*)Form->Components[i])->SetBounds(xt(sc[i].Left), yt(sc[i].Top), sc[i].Width, sc[i].Height); }; // Datei: d:\Loesungen\kap-9\9.3\ResizU.cpp
#include "ResizU.h" //-------------------------------------------------------------------#include "ResizeUnit.h" // Noch "ResizeUnit.cpp" mit Projekt|Dem Projekt hinzufügen Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.8 Lösungen Kapitel 9
TResize r; // Passt nur die Größe und nicht die Position an // CResize r(true); // Passt die Größe und die Position an void __fastcall TForm1::FormResize(TObject *Sender) { r.Resize(Form1); } //--------------------------------------------------------------------
12.8.3 Aufgabe 9.4
// Datei: d:\Loesungen\kap-9\9.4\EigKompU.cpp
#include "EigKompU.h" // ------- Aufgabe 1 -----------------------------------// ClickAction ist im Klassenheader deklariert void __fastcall TForm1::ClickAktion1(TObject *Sender) { Button3->Caption="Aktion 1"; } void __fastcall TForm1::ClickAktion2(TObject *Sender) { Button3->Caption="Aktion 2"; } void __fastcall TForm1::ClickAktion3(TObject *Sender) { Button3->Caption="Aktion 3"; } void __fastcall TForm1::RadioButton1Click(TObject *Sender) { Button3->OnClick=ClickAktion1; } void __fastcall TForm1::RadioButton2Click(TObject *Sender) { Button3->OnClick=ClickAktion2; } void __fastcall TForm1::RadioButton3Click(TObject *Sender) { Button3->OnClick=ClickAktion3; } // ------- Aufgabe 2 -----------------------------------TEdit* MakeEdit(TForm* F, int l,int t,int w,int h) { TEdit* E = new TEdit(F); E->Parent = F; E->SetBounds(l, t, w, h); return E; }; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
367
368
12 Lösungen
TLabel* MakeLabel(TForm* F, int l,int t,int w,int h, AnsiString Caption) { TLabel* L = new TLabel(F); L->Parent = F; L->SetBounds(l, t, w, h); L->Caption=Caption; return L; }; TButton* MakeButton(TForm* F,AnsiString Caption, int l,int t,int w,int h,TNotifyEvent E) { TButton* B = new TButton(F); B->Parent = F; B->SetBounds(l, t, w, h); B->Caption=Caption; B->OnClick=E; return B; }; class MyForm : public TForm { __published: // Von der IDE verwaltete Komponenten private: // Anwender-Deklarationen public: // Anwender-Deklarationen __fastcall MyForm(TForm* AOwner); void __fastcall EndeClick(TObject *Sender); void __fastcall LoeschenClick(TObject *Sender); void __fastcall SpeichernClick(TObject *Sender); TLabel* LVorname; TEdit* EVorname; TLabel* LNachname; TEdit* ENachname; TLabel* LBussgeld; TEdit* EBussgeld; TLabel* LOrdnungswidrigkeit; TEdit* EOrdnungswidrigkeit; TButton* BSpeichern; TButton* BLoeschen; TButton* BEnde; }; __fastcall MyForm::MyForm(TForm* AOwner):TForm(AOwner,0) { Caption="Das städtische Haushaltssanierungsprogramm"; int LabelLeft=10, EditLeft=150, Top=10, TopInc=40, w=100,h=20; LVorname=MakeLabel(this,LabelLeft,Top,w,h,"Vorname"); EVorname=MakeEdit (this,EditLeft,Top,w,h); LNachname=MakeLabel(this,LabelLeft,Top+TopInc,w,h,"Nachname"); ENachname=MakeEdit (this,EditLeft,Top+TopInc,w,h); LBussgeld=MakeLabel(this,LabelLeft,Top+2*TopInc,w,h,"Bussgeld"); EBussgeld=MakeEdit (this,EditLeft,Top+2*TopInc,w,h); LOrdnungswidrigkeit=MakeLabel(this,LabelLeft,Top+3*TopInc,w,h,"Ordnungswidrigkeit "); EOrdnungswidrigkeit=MakeEdit (this,EditLeft,Top+3*TopInc,w,h); TButton* BSpeichern=MakeButton(this,"Daten speichern",10,180,100,h,SpeichernClick); TButton* BLoeschen=MakeButton(this,"Eingabe löschen",120,180,100,h,LoeschenClick); TButton* BEnde=MakeButton(this,"Programm beenden",230,180,100,h,EndeClick); Show(); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.8 Lösungen Kapitel 9
SetBounds(0,0,400,250); }; void __fastcall MyForm::SpeichernClick(TObject *Sender) { AnsiString v=EVorname->Text; AnsiString n=ENachname->Text; AnsiString b=EBussgeld->Text; AnsiString o=EOrdnungswidrigkeit->Text; } void __fastcall MyForm::LoeschenClick(TObject *Sender) { EVorname->Clear(); ENachname->Clear(); EBussgeld->Clear(); EOrdnungswidrigkeit->Clear(); } void __fastcall MyForm::EndeClick(TObject *Sender) { Close(); } void __fastcall TForm1::CreateFormClick(TObject *Sender) { MyForm* MyForm1=new MyForm(Form1); }
12.8.4 Aufgabe 9.5
// Datei: d:\Loesungen\kap-9\9.5\MDIImgCU.cpp
#include "MDIImgCU.h" TMDIChildForm *MDIChildForm; __fastcall TMDIChildForm::TMDIChildForm(TComponent* Owner) : TForm(Owner) { } void __fastcall TMDIChildForm::FormCreate(TObject *Sender) { Image1->Align=alClient; FormStyle=fsMDIChild; } void __fastcall TMDIChildForm::FormClose(TObject *Sender, TCloseAction &Action) { Action=caFree; }
12.8.5 Aufgabe 9.5
// Datei: d:\Loesungen\kap-9\9.5\MDIImgPU.cpp
#include "MDIImgPU.h" Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
369
370
12 Lösungen
//-------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { FormStyle = fsMDIForm; } //-------------------------------------------------------------------#include "MDIImgCU.h" void __fastcall TForm1::ffnen1Click(TObject *Sender) { if (OpenPictureDialog1->Execute()) { MDIChildForm=new TMDIChildForm(this); MDIChildForm->Image1->Picture->LoadFromFile(OpenPictureDialog1->FileName); } } //--------------------------------------------------------------------
12.8.6 Aufgabe 9.5
// Datei: d:\Loesungen\kap-9\9.5\mdiapp.cpp
USEFORM("Main.cpp", MainForm); USEFORM("ChildWin.cpp", MDIChild); USERES("mdiapp.res"); USEFORM("about.cpp", AboutBox); WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { Application->Initialize(); Application->CreateForm(__classid(TMainForm), &MainForm); Application->CreateForm(__classid(TAboutBox), &AboutBox); Application->Run(); return 0; }
12.8.7 Aufgabe 9.6
// Datei: d:\Loesungen\kap-9\9.6\KlassRefU.cpp
#include "KlassRefU.h" //-------------------------------------------------------------------void ShowBaseNames(TMetaClass* M) { for (TMetaClass* C=M;C!=0; C=C->ClassParent()) Form1->Memo1->Lines->Add(C->ClassName()); } void __fastcall TForm1::Button1Click(TObject *Sender) { ShowBaseNames(__classid(TEdit)); } //--------------------------------------------------------------------
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.8 Lösungen Kapitel 9
12.8.8 Aufgabe 9.7
// Datei: d:\Loesungen\kap-9\9.7\BotschU.cpp
#include "BotschU.h" //------ Aufgabe 1 --------------------------------------------------void __fastcall TForm1::ButtonCopyClick(TObject *Sender) { SendMessage(Memo1->Handle, WM_COPY, 0, 0); // dasselbe Ergebnis erzielt man mit // Memo1->Perform(WM_COPY,0,0); } void __fastcall TForm1::ButtonCutClick(TObject *Sender) { SendMessage(Memo1->Handle, WM_CUT, 0, 0); // dasselbe Ergebnis erzielt man mit // Memo1->Perform(WM_CUT,0,0); } void __fastcall TForm1::ButtonPasteClick(TObject *Sender) { SendMessage(Memo1->Handle, WM_PASTE, 0, 0); // dasselbe Ergebnis erzielt man mit // Memo1->Perform(WM_PASTE,0,0); } //------ Aufgabe 2 --------------------------------------------------void TForm1::EditNext() { if (RadioButton1->Checked) RadioButton1->Caption="Mit Enter in Edit1/Edit2 nicht weiter"; else RadioButton1->Caption="Mit Enter in Edit1/Edit2 weiter"; if (RadioButton2->Checked) RadioButton2->Caption="Form KeyPreview true"; else RadioButton2->Caption="Form KeyPreview false"; Form1->KeyPreview=RadioButton2->Checked; if (RadioButton3->Checked) { RadioButton3->Caption="Edit1/Edit1->OnKeyPress=Edit1->OnKeyPress"; Edit1->OnKeyPress=Edit1KeyPress; Edit2->OnKeyPress=Edit1KeyPress; } else { RadioButton3->Caption="Edit1/Edit1->OnKeyPress=0"; Edit1->OnKeyPress=0; Edit2->OnKeyPress=0; } } void __fastcall TForm1::FormCreate(TObject *Sender) { RadioButton1->Checked=true; }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
371
372
12 Lösungen
void __fastcall TForm1::RadioButton2Click(TObject *Sender) { EditNext(); } void __fastcall TForm1::RadioButton3Click(TObject *Sender) { EditNext(); } void __fastcall TForm1::RadioButton1Click(TObject *Sender) { EditNext(); } void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key) { if (Key == 13) // falls KeyPreview auf true gesetzt ist Form1->Perform(WM_NEXTDLGCTL,0,0); // dasselbe Ergebnis erzielt man mit // SendMessage(Form1->Handle,WM_NEXTDLGCTL,0,0); } } void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key) { if (Key == 13) Form1->Perform(WM_NEXTDLGCTL,0,0); } #include "KomponU.h" #include "KomponU.cpp" //------- Aufgabe 3: TTabEdit ---------------------------------------void __fastcall TForm1::TabEditClick(TObject *Sender) { static int Left=GroupBox3->Left+TabEdit->Width+10; static int Top=GroupBox3->Top+10; Left=Left+20; Top=Top+20; TTabEdit* te=new TTabEdit(Form1,Form1,Left,Top,60,20); te->Text="TabEdit "+IntToStr(Left); } //------- Aufgabe 4: TFocusColorEdit --------------------------------void __fastcall TForm1::FocusColorEditClick(TObject *Sender) { TFocusColorEdit* fce=new TFocusColorEdit(Form1, GroupBox4->Left+FocusColorEdit->Width+20, GroupBox4->Top+FocusColorEdit->Top, 100, 30); fce->Parent=Form1; } //------- Aufgabe 5: TResizableMemo ---------------------------------void __fastcall TForm1::ResizMemoClick(TObject *Sender) { TResizableMemo* tr=new TResizableMemo(Form1,GroupBox6->Left+ResizMemo->Width+20, GroupBox6->Top+ResizMemo->Top,//+ColBordLabel->Top, 100, 100); tr->Text="Der rechte Rand kann mit der Maustaste verzogen werden"; Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.9 Lösungen Kapitel 10
373
} //------- Aufgabe 6: TColBorderLabel --------------------------------void __fastcall TForm1::ColBordLabelClick(TObject *Sender) { TColBorderLabel* tc=new TColBorderLabel(Form1,ColBordLabel->Left+ColBordLabel>Width+20, ColBordLabel->Top, 100, 20); tc->BlinkInterval=500; tc->BorderColor=clRed; tc->Color=clYellow; } //------- Aufgabe 7: TRubberShape -----------------------------------void __fastcall TForm1::RubberShapeClick(TObject *Sender) { TRubberShape* tr=new TRubberShape(Form1,RubberShape->Left+RubberShape->Width+20, RubberShape->Top, 150, 150); tr->Canvas->TextOut(0,0,""); }
12.9 Lösungen Kapitel 10
12.9.1 Aufgabe 10.1
// Datei: d:\Loesungen\kap-10\10.1\FktTemplU.cpp
#include "FktTemplU.h" //----- Aufgabe 1. -----------------------------------------------template void vertausche(T& a, T& b) { T h = a; a = b; b = h; } template bool kleiner(T x,T y) { return x // Wenn man die folgende Spezialisierung auskommentiert, wird ein // Array mit Zeigern nicht richtig sortiert: template bool kleiner(T* x,T* y) { // Form1->Memo1->Lines->Add(typeid(x).name()); Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“ | |