• Keine Ergebnisse gefunden

f: essen, pflücken, ...

e: Geschmack, Farbe, ...

Obst

Nahrungsmittel f: essen, ...

e: Geschmack, ...

e: Geschmack, Farbe, Sorte, ...

f: essen, pflücken, ...

Apfel

Wurfgeschoß f: ...

e: ...

f = Operationen, Funktionen e = Eigenschaften, Attribute

Abbildung 2.1: Begriffshierarchien

2.2 Objekthierarchien als strukturierte

Modulsammlungen: Beispiele aus Eiffel

Objektorientierte Programmiersprachen erm¨oglichen die Datenkapselung und eine evolution¨are Programmerstellung:

Nutze vorhandene Objektklassen (Typen) oder erzeuge neue Ob-jektklassen, wobei bei Teilstrukturgleichheit m¨oglichst viel durch Vererbung existierender Klassen realisiert wird.

2.2.1 Vererbung und Erweiterung

Namens¨anderung

Neues feature

class Multiindexinherit

ARRAY[CARDINAL] rename

count as Dimension, clear all asNull end;

feature

abs: CARDINAL is require not empty do ...

ensure

- - abs = For all i:lower..upper:

SUM item(i) end- - abs

end- - class Multiindex

2.2.2 Vererbung und Ab¨ anderung

alternative Implementie-rung −→

alternative Implementie-rung −→

auch die Vorbedingung wird geerbt−→

zus¨atzliche Nachbedin-gung −→

alte Invariante wird ge-erbt −→

class Multiindex inherit

ARRAY[CARDINAL]rename

count asDimension, clear all as Null redefine

put(v:like item; i:INTEGER)

- - replace i-th entry, if in index interval, by v ...

- - abs = For all i:lower..upper:Sum item(i) end - - class Multiindex

Wenn aus Effektivit¨atsgr¨unden redundante Daten angelegt werden, so sollten diese Redundanzen spezifiziert werden!

Es gelten folgende Regeln bei der Vererbung (von is-a-Methoden):

a) Vorbedingungen k¨onnen in einer Kindklasse abgeschw¨acht werden.

b) Nachbedingungen in einer Kindklasse m¨ussen st¨arker sein als diejenigen der Elter-klasse.

c) Invarianten in der Kindklasse m¨ussen ebenfalls st¨arker als in der Elterklasse sein.

Dann ist ein echtes Subcontractingrealisiert.

Bemerkung: Es reicht die Kindnachbedingung im Falle des Eintreffens der Eltervorbe-dingung st¨arker als die ElternachbeEltervorbe-dingung zu realisieren. Im Falle

”Kindvorbedingung and not Eltervorbedingung“ darf die Kindnachbedingung frei gew¨ahlt werden.

2.2.3 Generizit¨ at

class STACK[T]

feature ...

end - - class STACK[T]

2.2.4 Eingeschr¨ ankte Generizit¨ at

class VECTOR[T –> ADDABLE]

feature ...

end- - class VECTOR

2.2.5 Polymorphie und

” late binding“

class Rectangle inherit

POLYGONredefine perimeter end

feature{NONE}

side1: REAL;

side2: REAL;

feature{ANY}

perimeter: REAL is do

Result := 2∗(side1 + side2) end - - perimeter

...

end- - class Rectangle

Wegen des Zusammenhangs Rectangle ⊂ POLYGON und MethodenVon(Rectangle) ⊃ MethodenVon(POLYGON) gilt in der Anwendung:

perimeter f¨ur die Menge aller Erben von POLYGON

r : Rectangle;

...

!!p; !!r;

...

print (p.perimeter); - - perimeter aus

... - - POLYGON

p:=r

print (p.perimeter); - - perimeter aus

... - - Rectangle

2.2.6 Aufgeschobene Feature-Implementierungen

deferred class Stack[T]

feature

nb elements : INTEGER is defered

end - - nb elements empty : BOOLEAN is do

Result := (nb elements = 0) ensure Result = (nb elements = 0) end - - empty

...

end - - class STACK[T]

... dienen der partiellen Implementierung einer Gruppe m¨oglicher Implementierungen (Schablone). Sie stehen somit in Konkurrenz und erg¨anzen generische Klassen.

– ”Objektorientiertes” Programmieren (als Alternative zum funktionalen Top-Down-Entwurf und zum datengesteuerten Entwurf nach Jackson) ist die Softwa-rekonstruktion mit Hilfe der Adaption von Sammlungen abstrakter Datentyp-Implementierungen.

– Unterklassen k¨onnen sich von ihren Basisklassen unterscheiden durch:





1) mehr Operationen 2) mehr Daten (Attribute)

3) eingeschr¨ankte Wertebereiche der Daten 4) alternative Implementierungen

2.3 Res¨ umee

1. Objektorientiertes Programmieren setzt eine gute Kenntnis der vorhandenen Klas-senhierarchien voraus! Diese sind heute jedoch h¨aufig nicht ausreichend dokumen-tiert (fehlende Spezifikation, fehlende Fixierung der Design-Ideen, ...). H¨aufig steht nur ein Browser zur Betrachtung der Quellen der Klassen zur Verf¨ugung, und der Programmierer muß sich selbst den Durchblick durch die Konzeption der Klassen-bibliotheken erk¨ampfen.

2. Einige objektorientierte Sprachen bieten gar keine mitgelieferten Klassenbiblio-theken an. Andere haben sprachspezifisch bzw. sogar herstellerspezifisch eigene — zwar h¨aufig an Smalltalk angelehnte, aber dennoch in wichtigen Details abwei-chende — Klassenhierarchien. F¨ur viele Gebiete in der Informatik/Mathematik/

Anwendungswissenschaft fehlen geeignete Klassenbibliotheken g¨anzlich.

3. Geordnete evolution¨are objektorientierte Entwicklung im Team erfordert ein richtiges Management (open-close-Phasen, Versions-Management, ...)

4. Objektorientierte Programmiersprachen sollten syntaktische Sprachmittel f¨ur Zu-sicherungenbesitzen (mindestens Aussagenlogik, besserPr¨adikatenlogik). Die-se sollteninden gefordertenKlassenhierarchien(zumindest in Kommentarform) intensiv genutzt werden. Eine etwa VDM ¨ahnliche Syntax w¨are gewiß inter-essant.

Wir m¨ussen anspruchsvoller werden im Hinblick auf die Verl¨aßlichkeit und die Qualit¨at unserer Software. Die Benutzer m¨ussen kritischer werden und weniger bereit, Software-erzeugnisse geringer Qualit¨at zu akzieptieren.

(Zitat: Robert L. Baber: Softwarereflexionen, Springer-Verlag)

Forschungsministerium f¨ ordert Standard f¨ ur IT-Sicherheit

Trotz des fl¨achendeckenden Einsatzes von Computersystemen in sicherheitsrelevanten Bereichen fehlt bislang eine standardisierte Methode, die das fehlerfreie Funktionie-ren solcher Systeme garantiert. Das Bundesministerium f¨ur Bildung und Forschung (BMBF) will nun Arbeiten f¨ordern, bei denen mit Methoden der Verifikation der so genannte geschlossene integrierte Korrektheitsbeweis erbracht werden kann. Damit sollen sich Fehler bereits im Entwurf von autonomen oder integrierten Computersys-temen erkennen und korrigieren lassen – eine sorgf¨altige Spezifikation vorausgesetzt.

Alle m¨oglichen Fehlersituationen k¨onnten aber nur dann abgefangen werden, wenn bereits in der Planung die entsprechenden Einsatzszenarien definiert wurden, betonte Projektleiter Prof. Dr. Wolfgang Paul gegen¨uber heise Security.

F¨ur die erste zweij¨ahrige Forschungsphase werde das BMBF 7,2 Millionen Euro zur Verf¨ugung stellen, teilte das Ministerium am heutigen Mittwoch in Berlin mit. An dem Projekt beteiligen sich neben der Universit¨at Saarland unter anderen auch die TUs Darmstadt, Karlsruhe, M¨unchen sowie Infineon, T-Systems und BMW.

Die Entwicklung eines integrierten Korrektheitsbeweises gilt zurzeit als eine der gr¨oßten Herausforderungen der Informatik. Er soll die Funktionen bei der Entwicklung von Hard- und Systemsoftware bis zur Netzwerk- und Anwendungsebene laufend uberpr¨ufen. Zun¨achst sollen die mathematischen Grundlagen entwickelt, vollst¨andig¨ formalisiert und f¨ur Informatikanwendungen in den Bereichen Embedded Systems, Kommunikation und Anwendungssoftware erschlossen werden. Darauf aufbauend sollen die Projektpartner Demonstratoren entwickeln und mit ihnen Computersysteme f¨ur Chipkarten, Telekommunikation und Automobilelektronik von der Hardware bis zur Anwendungssoftware ¨uberpr¨ufen. Im Rahmen des Projektes werden auch Softwaretools entwickelt, die den Verifikationsprozess unterst¨utzen. (dab/c’t)

Link: http://www.heise.de/newsticker/data/dab-01.10.03-002/

Siehe auch (Thema Produkthaftung):

http://www.heise.de/newsticker/result.xhtml?url=/newsticker/meldung/86839

3 Programming by Contract

3.1 Spezifikation durch Vertr¨ age

(SdV,Design by Contract1,Programming by Contract) ist eine Methode zur Spezifika-tion der dynamischen Semantik von Softwarekomponenten mit Hilfe von Vertr¨agen aus erweiterten boolschen Ausdr¨ucken. SdVbasiert auf der Theorie der abstrakten Datenty-pen und formalen Spezifikationsmethoden. Spezifizierte Komponenten k¨onnen Module, Klassen oder Komponenten im Sinne von Komponententechnologien (wie Microsofts COM, .NET oder Suns EJB) sein. Vertr¨age erg¨anzen das Kunden-Lieferanten-Modell:

Kunde

schließen Vertrag

kooperieren gemäß Vertrag

Lieferant

Abbildung 3.1: Kunden-Lieferanten-Modell

Grundlegend f¨ur die Vertragsmethode ist das Prinzip der Trennung von Diensten in Abfragen und Aktionen (command-query separation):

• Abfragen geben Auskunft ¨uber den Zustand einer Komponente, ver¨andern ihn aber nicht. Sie liefern als Ergebnis einen Wert. Die Abfragen einer Komponente beschreiben ihren abstrakten Zustand.

• Aktionen ver¨andern den Zustand einer Komponente, liefern aber kein Ergebnis.

Die Aktionen einer Komponente bewirken ihre Zustandsver¨anderungen.

Diesem Prinzip folgend sind seiteneffektbehaftete Funktionen als Dienste zu vermeiden2.

1”Design by Contrakt”ist ein Warenzeichen von Interactive Software Engeneering.

2In bestimmten F¨allen, z.B. bei Fabrikfunktionen, k¨onnen Seiteneffekte sinnvoll sein. Solche Funktio-nen sind nicht als Spezifikatoren verwendbar und sollten entsprechend gekennzeichnet sein.

Ein Grund daf¨ur ist, dass Abfragen als Spezifikatoren dienen, d.h. als Elemente von Vertr¨agen. Vertr¨age setzen sich aus Bedingungen folgender Art zusammen:

• Invarianteneiner Komponente sind allgemeine unver¨anderliche Konsistenzbedin-gungen an den Zustand einer Komponente, die vor und nach jedem Aufruf eines Dienstes gelten. Formal sind Invarianten boolsche Ausdr¨ucke ¨uber den Abfragen der Komponente; inhaltlich k¨onnen sie z.B. Gesch¨aftsregeln (busisness rules) aus-dr¨ucken.

• Vorbedingungen (preconditions) eines Dienstes sind Bedingungen, die vor dem Aufruf eines Dienstes erf¨ullt sein m¨ussen, damit er ausf¨uhrbar ist. Invarianten sind boolsche Ausdr¨ucke ¨uber den Abfragen der Komponente und den Parametern des Dienstes.

• Nachbedingungen (postconditions) eines Dienstes sind Bedingungen, die nach dem Aufruf eines Dienstes erf¨ullt sind; sie beschreiben, welches Ergebnis ein Dienstaufruf liefert oder welchen Effekt er erzielt. Nachbedingungen sind boolsche Ausdr¨ucke ¨uber den Abfragen der Komponente und den Parametern des Dienstes, erweitert um ein Ged¨achniskonstruckt, das die Werte von Ausdr¨ucken vor dem Dienstaufruf liefert.

Vertr¨age legen Pflichten und Nutzen f¨ur Kunden und Lieferanten fest. Die Verantwort-lichkeiten sind klar verteilt:

Der Lieferant garantiert die Nachbedingung jedes Dienstes, den der Kunde aufruft, falls der Kunde die Vorbedingung erf¨ullt. Eine verletzte Vorbedingung ist ein Fehler des Kun-den, eine verletzte Nachbedingung oder Invariante (bei erf¨ullter Vorbedingung) ist ein Fehler des Lieferanten.

Kunde Lieferant

Pflicht Die Vorbedingung einhalten. Anweisungen ausf¨uhren, die die Nachbedingungen herstellen und die Invarianten erhalten

Nutzen Ergebnisse/Wirkungen nicht pr¨ufen, da sie durch die Nachbe-dingungen garantiert sind.

Aufrufe, die die Vorbedingung verletzen, ignorieren. (Die Vorbe-dingungen nicht pr¨ufen.)

Tabelle 3.1: Pflichten - Nutzen von Kunden und Lieferanten

Schwache Vorbedingungen erleichtern den Kunden die Arbeit, starke Vorbedingungen dem Lieferanten. Je schw¨acher die Nachbedingungen sind, umso freier ist der Lieferant und umso ungewisser sind die Kunden ¨uber das Ergebnis/den Effekt. Je st¨arker die Nachbedingungen sind, umso mehr muß der Lieferant leisten.

Siehe auch:

Spezifikation durch Vertrag — eine Basistechnologie f¨ur eBusiness

3.2 Invarianten

Set (IN newN:INTEGER) PRE

newN >0 POST

N = newN END Natural

Cleo unterscheidet Abfragen und Aktionen durch die QUERIES- und ACTIONS- Ab-schnitte. Die Invariante N>0 im INVARIANTS- Abschnitt schr¨ankt den Wertebereich der Abfrage N ein. DasPrinzip des einheitlichen Zugriffs(uniform access) verlangt, von der Implementierung parameterlose Abfragen zu abstrahieren: N ist als Attribut oder parameterlose Funktion implementierbar, der Zugriff auf N wird davon unabh¨angig notiert.

Die Aktion Set (Konstruktor) bewirkt, dass N nach einem Aufruf von Set den als Pa-rameter newN ¨ubergebenen Wert liefert. Die PRE- und POST- Abschnitte f¨ur die Vor-und Nachbedingungen spezifizieren diese Semantik. Da Set die Invariante nicht verletzen darf, schr¨ankt es durch die Vorbedingung den akzeptablen Wertebereich von newN ein.

3.3 Nachbedingungen

Als zweites Beispiel dient eine parametrisierte Abfrage. Die Fakult¨atsfunktion Factorial k¨onnte Teil einer Komponente mit mathematischen Funktionen sein:

QUERIES

Factorial (IN n : INTEGER) : INTEGER PRE

N >= 0 POST

(n>=0) IMPLIES (result=1)

(n>1) IMPLIES (result=n*Factorial (n - 1))

Zum Formulieren der Nachbedingung braucht man einen Namen f¨ur das Ergebnis des Funktionsaufrufs (result).

3.4 Vorbedingungen

Das dritte Beispiel modelliert eine (mathematische) Menge als generische Komponente Set: Element ist der generische Parameter, der als Elementtyp fungiert.

INTERFCE Set[Element]

QUERIES

Count:INTEGER

- - Number of elements in the set.

Has(IN x:Element):BOOLEAN - - Does the set contain x?

POST

result IMPLIES(Count>0) IsEmpty:BOOLEAN

- - Does the set contain no element?

INVARIANTS

OLD (Has(x)) IMPLIES (Count=OLD(Count))

NOT OLD (Has(x)) IMPLIES (Count=OLD(Count)+1) Remove(IN x:Element)

- - Exclude x from the set.

POST

NOT Has(x)

OLD (Has(x)) IMPLIES (Count=OLD(Count)-1) NOT OLD (Has(x)) IMPLIES (Count=OLD(Count)) WipeOut

- - Exclude all elements from the set.

POST Count=0 END Set

Um das Verhalten der Aktionen Put und Remove, des Hinzuf¨ugens eines Elementes zur Menge und des Entfernens eines Elementes aus der Menge, zu spezifizieren, ist der

Zustand der Menge vor einem Aktionsaufruf mit ihrem Zustand nach dem Aktionsaufruf zu vergleichen. Das Vorzustandskonstruckt erm¨oglicht dies: Mit OLD (...) geklammerte Ausdr¨ucke liefern den Wert des geklammerten Ausdrucks vor einem Dienstaufruf.

OLD-Ausdr¨ucke d¨urfen nur in Nachbedingungen auftreten. der Ausdruck Count=OLD(Count)+1

bedeutet, das sich der Wert von Count durch die Ausf¨uhrung des Dienstes um 1 erh¨oht.

Dabei muss es sich um eine Aktion handeln, denn da eine Abfrage q den Zustand ihrer Komponente unver¨andert l¨asst, gelten f¨ur sie Nachbedingungen der Art

q=OLD(q)

mit beliebigem Attribut q. Nachbedingungen, die ausdr¨ucken, was sich nicht ¨andert, l¨asst man meist weg.

4 Qualit¨ atssicherung mit normalen C++ Sprachmitteln

4.1 Umgangssprarchliche Spezifikation?

”Informelle Beschreibung“: Auf einem Parkplatz stehen PKW’s und Motor¨ader.

Zusammen seien es n Fahrzeuge mit insgesamt m R¨adern. Bestimme die Anzahl P der PKW’s.

”L¨osung“: Sei

P := Anzahl der PKW’s M := Anzahl der Motor¨ader

P +M =n

Problem: ****Null-Euro-Rechnung, Null-Euro-Mahnung,...

(m, n) = (9,3)⇒P = 112 (m, n) = (2,5)⇒P =−4

Vor der Entwicklung eines Algorithmus ist zun¨achst f¨ur das Problem eine Spezifikation bestehend aus

1. Definitionsbereich, 2. Wertebereich und

3. f¨ur die L¨osung wichtigen Eigenschaften (insbesondere funktionaler Zusammenhang zwischen Eingabe- und Ausgabegr¨oßen)

anzufertigen.

Besser ist also:

Eingabe: m, n∈ {0,1, ..., IN T M AX} Vorbedingungen: m gerade, 2n≤m≤4n

Ausgabe: P ∈ {0,1, ..., IN T M AX}, falls die Nachbedingung erf¨ullt ist (sonst

”keine L¨osung“)

Nachbedingung:Ein (P, M)∈ {0,1, ..., IN T M AX} mit P +M =n

4P + 2M =m

4.2 Unbeachtet - Integer-Overflows in C/C++

(Vermeidung von Qverflows)

”Spezifikation“ von Variablen so genau wie in der benutzten Programmiersprache m¨oglich:

const

ErrorResult = -1;

functionfakultaet(n : integer{n >= 0}): integer;

var

zaehl : 2..maxint;

teilres : 1..maxint; {Teilresultat}

begin

teilres := 1;

for zaehl := 2 to n do begin teilres := teilres * zaehl end;

if (n>= 0) then fakultaet := teilres else fakultaet := ErrorResult;

end;

Verifikation:

Endlichkeit: Die Schleife wird genau (n-1)-mal durchlaufen, falls n ≥ 2. Ansonsten wird sie kein mal durchlaufen.

Schleifenvariante: Zerlegung des gew¨unschten Ergebnisses n! im ein schon berech-netes Teilergebnis teilres und einen noch zu berechnenden Rest:

k |

Korrektheit bei Abbruch:

begin

{n in integer; zaehl, telres undef}

telres := 1

{n! = teilres * n!}

for zaehl := 2 to n do begin {n >= 2}

{ n! = teilres · Qn

i=zaehli, n≥ zaehl } teilres := teilres * zaehl

{n! = teilres · Qn

i=zaehl+1i, n ≥zaehl}

end;

{n! ≥2: n!=teilres*1, zaehl undef}

{n! ≥ 2: n!=teilres; n in [0,1]: teilres = 1 = n!}

{n < 0:n! undef}

...

Informelle Spezifikation:

Zweck:

”fakultaet“ berechnet die Fakult¨at von n: n! = 1∗2∗3∗...∗(n−1)∗n, 0! = 1

f¨ur alle n in [0,1,...,maxint], f¨ur dien! auch in INTEGER liegt.

Vorbedingungen:

n ∈integer Nachbedingungen:

fakultaet liefert:

n!, falls n≥0 undn!≤maxint;

ErrorResult, falls n <0

undefiniertes Verhalten bei n!>maxint.

Beachte:

Diese Spezifikation ist f¨ur die Praxis ungeeignet!

”Verhalten undefiniert“ h¨angt von Pascal und der Laufzeitumgebung ab (Overflow-Exception ja/nein).

M¨ogliche Varianten/Verbesserungen

Ziel: Berechne teilres := Teilres * zaehl nur dann, wenn das innerhalb [1,...,maxint]

m¨oglich ist.

... {n! = teilres ·Qn

i=zaehli, n ≥ zaehl}

{teilres <= maxint}

if (maxint div teilres >= zaehl) then {teilres * zaehl <= maxint}

teilres := teilres * zaehl {n! = teilres ·Qn

i=zaehl+1i, n ≥zaehl}

else begin

{n! ≥ teilres * zaehl >maxint}

...

Ahnlich:¨ Etwa bei einer verbesserten Version von

”Power“ unter Zugrundelegung von:

Teilerg := 1.0;

while (n >0) do if odd(n) then begin

Teilerg := Teilerg * x; n := n-1;

end else begin

x := sqr(x); n:= n div 2;

.. end;

.

Im Algorithmus nach O.J. Dahl/ E.W. Dijkstra/C.A.R. Hoare: Structured Programming auf Seite 14 wird eine Vermeidung von Fallunterscheidungen auf folgende Weise angestrebt:

... begin

Teilerg := 1.0;

while (n > 0) do begin if odd(n) then begin

Teilerg := Teilerg * x; n := n-1;

end; {n gerade}

x := sqr(x); n:= n div 2;

... end;

Obwohl diese drei Varianten in (R,Z0+) alle das richtige Ergebnis xn liefert, wird in der dritten Variante am Schluß eine unn¨otige Quadierung von x vorgenommen(while-Statement mitn= 1). Das kann zu einem Overflow f¨uhren, obwohl der durch das entspre-chende x := sqr(x) berechnete x-Wert f¨ur die gesuchte Potenz gar nicht mehr ben¨otigt wird!

Es wird also f¨ur Variante 3 eine strengere Vorbedingung als n¨otig und w¨unschenswert gefordert(d.h. der Bereich der Argumente, f¨ur die xn berechnet wird, ist unn¨otigerweise verkleinert worden):

- Vorbedingung zu Variante 1/2: xn ist [im Rahmen der begrenzten Rechner-genauigkeit in IEEE real) kleiner oder gleich FLT MAX.

- Vorbedingung zu Variante 3:xnundx2[log2(n)]+1ist [im Rahmen der begrenzten Rechnergenauigkeit in IEEE real) kleiner oder gleich FLT MAX.

Es muß also f¨ur die Variante 3 nicht nurxn, sondern auch die Potenz von xzum Expo-nenten der zu n n¨achsth¨oheren Zweierpotenz kleiner oder gleich FLT MAX sein.

1. ”Vermeide unn¨otigen Overflow in der Zwischenrechnung:“

n := n div 2; if (n<> 0) then x := sqr(x)

2. ”Fange unvermeidlichen Overflow bei der Berechnung ab:“

a) ... bei der Berechnung von sqr(x):

n := n div 2;

if (n <> 0) then

if (x < SQR FLT MAX) then

↑ selbst zu definierende Konstante aus FLT MAX (float.h,ect.)

x := sqr(x) else ...;

b) ... bei der Berechnung von Teilerg * x:

if ((MAX FLT/Teilerg) >= x) then Teilerg := Teilerg * x;

4.3 Vergessene Problematik: unordered floats — IEEE NaNs

In

y1 = 0;

y2 = 0;

for (i=0; i<ITERATIONS; i++) {

x1 = y1;

x2 = y2;

y1 = x1 * x1 - x2 * x2 + c1;

y2 = 2.0 * x1 * x2 + c2;

n2 = y1 * y1 + y2 * y2;

if (n2> 4.0) then /* skip */

else

plot(y1,y2);

}

werden leider auch viele Punkte, an denen die Reihe divergiert, gezeichnet. Warum?

(Konvergente Iterationspunkte sollen geplottet werden. Ein Wert gr¨oßer als 4.0 kann als Divergenzkriterium gewertet werden. Im Falle ∞ − ∞ = N aN ist jedoch der Fall der Divergenz ohne Erf¨ullung von n2>4.0 gegeben.)

Eine Modifikation zu

else if unordered (n2,4.0) then /* skip */

else

plot(y1,y2);

beachtet

”unordered“ Argumente!

4.4 assert in C/C++

# include <assert.h>

...

int fakult¨at(int i) {

assert (i>= 0); /* Vorbedingung */

...

assert(result>0); /* Nachbedingung */

} liefert

”Assertion failed: file ass.c, line 15“ bei fakultaet(17) statt dem falschen Ergebnis -28522240.

Nach dem Austesten eventuelles Abstellen der ¨Uberpr¨ufungen durch

# define NDEBUG

# include <assert.h>

...

und erneute Compilation oder ¨Ubersetzung mittels g++ -NDEBUG ...

Ein Beispiel in C++:

# include <cassert>

// ...

. . .

assert(i<= DIM);

// ...

4.5 Vermeidung von enum

Der enum-Typ ist ein voller Interger-Datentyp und deshalb nur bedingt zur Anwendung zu empfehlen.

Zwar erlaubt er aussagekr¨aftige Namen f¨ur die zugeh¨origen Konstanten, ja sogar das Uberladen von Operatoren (hier zum Beispiel des Inkrementoperators),¨

#include <iostream>

#include <string>

using namespace std;

enum Day {Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag};

Day& operator++(Day& d) {

return d = (Sonntag == d) ? Montag : Day(d + 1);

}

string DayTable[] = {"Montag", "Dienstag", "Mittwoch", "Donnerstag",

"Freitag", "Samstag", "Sonntag"};

int main() {

Day Tag(Montag);

for (int i = 0; i < 15; i++)

cout << ++Tag << " " << DayTable[int(Tag)] << endl;

}

er verhindert aber nicht die Benutung von unsinnigen Integer-Operationen (hier zum Beispiel der Multiplikationen von Wochentagen).

Ausweg bietet nur die Einf¨uhrung einer eigenen Klasse nach dem folgenden Muster:

#include <iostream>

#include <sstream>

#include <string>

using namespace std;

namespace DayNS { class Day{

private:

enum DayType {_Montag, _Dienstag, _Mittwoch, _Donnerstag, _Freitag, _Samstag, _Sonntag};

static const string DayTable[7];

DayType t;

Day(const DayType& dt): t(dt) {};

public:

static const Day Montag;

static const Day Dienstag;

static const Day Mittwoch;

static const Day Donnerstag;

static const Day Freitag;

static const Day Samstag;

static const Day Sonntag;

Day(const Day& d = Montag): t(d.t) {};

Day& operator++();

const Day operator++(int);

friend istream& operator>>(istream&, Day&);

friend ostream& operator<<(ostream&, const Day&);

};

const Day Day::Montag(_Montag);

const Day Day::Dienstag(_Dienstag);

const Day Day::Mittwoch(_Mittwoch);

const Day Day::Donnerstag(_Donnerstag);

const Day Day::Freitag(_Freitag);

const Day Day::Samstag(_Samstag);

const Day Day::Sonntag(_Sonntag);

const string Day::DayTable[] = {"Montag", "Dienstag", "Mittwoch", "Donnerstag",

"Freitag", "Samstag", "Sonntag"};

Day& Day::operator++() {

(*this).t = (_Sonntag == t) ? _Montag : DayType((*this).t + 1);

return *this;

}

const Day Day::operator++(int) {

Day old_value(*this);

++(*this);

return old_value;

}

istream& operator>>(istream& is, Day& d) {

string s;

is >> s;

for (int i = 0; i < 7; i++)

if (s == Day::DayTable[i]) { d.t = Day::DayType(i);

return is;

}

// falscher Eingabestring:

is.putback(’ ’);

ostream& operator<<(ostream& os, const Day& d) {

os << Day::DayTable[int(d.t)];

return os;

} }

int main() {

DayNS::Day d1;

namespace d = DayNS;

d::Day d3;

using DayNS::Day;

Day d2(DayNS::Day::Sonntag);

using namespace DayNS;

cout << d1 << endl;

cout << d2 << endl;

d1 = Day::Montag;

for (int i = 0; i < 15; i++) cout << ++d1 << endl;

cout << endl;

d1 = Day::Montag;

for (int i = 0; i < 15; i++) cout << d1++ << endl;

cout << endl;

}

Zum Vermeiden eines eventuell ungew¨unschten Kopierkonstruktors beziehungsweise Zu-weisungsoperators vergleiche:

http://www.mozilla.org/hacking/portable-cpp.html#copy constructors

4.6 Compiletime Assertions

Um ein Programm nur auf solchen Compilern ¨ubersetzbar zu machen, auf dem der Typ intmindestens 16 Bit Genauigkeit bietet, kann man folgenden Code in sein Programm einbauen:

CT ASSERT(sizeof(int) * CHAR BIT >= 16, INT TO SMALL) Die Compilation bricht im Fehlerfalle ab mit einer Meldung der Art:

”example.cc”, line 21 : Error:

Cannot cast from ERROR IN T T O SM ALL to CompileTimeChecker < 0>. 1 Error(s) detected.

Insbesondere sind solche CT ASSERTs in der Deklaration von Templates n¨utzlich, um sinnvolle Fehlermeldungen zu erhalten.

CT ASSERT ist dabei etwa wie in

#include <iostream>

#include <iomanip>

using namespace std;

template<bool> struct CompileTimeChecker {

CompileTimeChecker(...);

};

template<> struct CompileTimeChecker<false> {};

#define CT_ASSERT(expr, msg)\

{\

class ERROR_##msg{};\

(void)sizeof(CompileTimeChecker< (expr) != 0 > ((ERROR_##msg())));\

}

int main() {

CT_ASSERT(LDBL_DIG > DBL_DIG, No_Higher_Accuracy);

return 0;

}

/**************************************************

"ct_asert_example.cc", line 21: Error:

Cannot cast from ERROR_No_Higher_Accuracy to CompileTimeChecker<0>.

1 Error(s) detected.

**************************************************/

zu definieren.

Siehe auch:

http://blogs.geekdojo.net/pdbartlett/archive/2004/05/19/TemplateMetaProgramming.aspx

4.7 Ausnahmebedingungen: Exceptions/Traps

Das Codest¨uck:

. . .

double power1(double x, intexp) {

double erg(1.0);

if (exp <0 ) throw(exp);

. . . }

erzeugt bei Nichterf¨ullen der Vorbedingung eine (abfangbare) Ausnahmebedingung (Exception) des Typs int.

Exceptions sollten in Handlern (catch-Anweisungen) abgefangen werden:

try{

//...

} catch(const char* err){

cerr << endl << ”### Fehler: ”<< err << endl;

exit(1);

} catch(const string& err){

//...

} catch(const int& i err){

//...

} catch (...){

cerr << endl << ”### Fehler: unbekannte Exception”<< endl;

exit(2);

}

Dabei m¨ussen speziellere vor allgemeineneren Exceptions abgefangen werden:

//...

#include <exception>

//...

try{

//...

if (exp< 0)throw range error(”exp invalid, should be>= 0”);

//...

}catch(const std::range error& re){

cerr << endl << ”### Fehler: ”<< re.what() << endl;

exit(1);

}catch(const std::bad alloc& bae){

//...

}catch(const std::exception& e){

//...

}catch (...){

cerr << endl << ”### Fehler: unbekannte Exception”<< endl;

exit(2);

}

wobei folgende standartm¨aßig vorhandene exception-Hierarchie benutzt wurde:

- exception - - bad_alloc - - bad_exception

- exception - - bad_alloc - - bad_exception

ÄHNLICHE DOKUMENTE