next up previous contents
Next: Die Klasse COMPLEX Up: KlassenKonstrukturen und Destrukturen: Previous: Die Abstraktion - Loslösung

Die Klasse STRING

/*    (str.h)        */
      #ifndef STR_H
      #define STR_H
      
      /* Die Klasse STRING wird abstrakt repraesentiert 
      
      
         Dies ist die Schnittstelle der Klasse */
      
      extern const void * STRING;
      
      /*
         Elementfunktionen der Klasse
      */
      
      void   setvalue ( void * self, char * chars);
         /* Stringzuweisung */
      char * getvalue ( void * self);
         /* String auslesen */
      char   getindex ( void * self, int i);
      void   setindex ( void * self, int i, const char c);
         /* Zugriff auf einzelne Zeichen */
      
      #endif
________________________________________________________________

/*    (main.c)      */
      #include <stdio.h>
      #include "new.h"
      #include "str.h"
      
      
      
      int main()
      {
        void * s = new( STRING ,1, "Hallo");
        void * t = new ( STRING , 0);
        void * u = copy(s);
        printf ("%s =s\n", getvalue(s));
        printf ("%s =t\n", getvalue(t));
        setvalue(s,"nochmal Hallo");
        printf ("%s =neues s\n", getvalue(s));
      
        delete(s);
        delete(t);
      
      
        cout(u);
        printf(" =u\n");
        setindex(u,0,'X');
        cout (u);
        printf(" u nach setindex\n");
        printf("Index 4 von u: %c\n", getindex(u,4));
      
        delete (u);
        return 0;
      }

Die Implementierung von STRING:

      #include <stdio.h>
      #include <stdarg.h>
      #include "str.h"
      #include "new.h"
      #include "error.h"
      
      
      /* Die Implementierung der Klasse STRING */
      
      /* Deklarationen der Elementfunktionen von STRING */
      
      static void * String_constructor (void * self, const int argnr, va_list * app);
      static void * String_destructor  (void * self);
      static void * String_copy        (const void * self);
      static void   String_setvalue    (void * self , char * chars);
      static char * String_getvalue    (void * self);
      static void   String_setindex    (void * self, int index, const char c);
      static char   String_getindex    (void * self, int index);
      static void   String_cout        (const void * self);
      
      
      /* Definition der Klasse, orientiert sich an 'class.d'  */
      
      struct Class_Str {
      
      /* die grundlegenden Methoden, fuer alle Klassen gleich */
      
        size_t size;
        void * (*constructor) (void * self, const int argnr, va_list * app);
        void * (*destructor)  (void *self);
        void * (*copy)        (const void * self);
        void   (*cout)        (const void *self);
      
      /* ab hier stehen die benutzerdefinierten Methoden */
      
        void   (*setvalue) (void * self, char * chars);
        char * (*getvalue) (void * self );
        void   (*setindex) (void * self, int index, const char c);
        char   (*getindex) (void * self, int index);
      };

Die Deklarationen der statischen Funktionen am Anfang sind nur für den Compiler notwendig, sie haben sonst keine Bedeutung.

An dieser Stelle definieren wir jetzt unsere Klasse STRING. STRING hat die in 'class.d' definierten Komponenten der abstrakten Klasse CLASS (notwendig für Kompatibilität), desweiteren besitzt die Klasse noch Methoden, um auf die Daten zugreifen zu können.

      
      struct String {
        const void * class;
        char * text;
      };

Die Definition der Objektstruktur. Ein Objekt zeigt auf seine Klasse und enthält seine eigenen Daten.

      static const struct Class_Str _String = { 
         sizeof(struct String),
         String_constructor, 
         String_destructor,
         String_copy,
         String_cout,
         String_setvalue,
         String_getvalue,
         String_setindex,
         String_getindex
      };

Die Erzeugung der Klasse STRING. Eine Klasse wird implementiert, indem genau ein statisches Element von Class_Str erzeugt wird, das zur gesamten Programmlaufzeit erhalten bleibt. Initialisiert werden müssen hier vor allem der Konstruktor und die Komponente size, da ohne diese beiden Eigenschaften kein Objekt erzeugt werden kann. Die anderen Methoden könnten auch vom Konstruktor gesetzt werden; falls man sie nicht braucht (z.B. falls eine Klasse keine Ausgabefunktion benötigt), können sie auch mit 0 initialisiert werden (beachte: definiert werden müssen sie, sofern CLASS sie auch besitzt).

      
      const void * STRING = & _String;

Die Erzeugung der Schnittstelle der Klasse STRING. Eine Klasse wird identifiziert über die Adresse ihres statischen Elements. Diese konstante Variable ermöglicht uns den Zugriff auf die Elementfunktionen der Klasse.

Die Elementfunktionen sind static, da sie nur innerhalb von str.c sichtbar sein müssen.

      
      /* Realisierung der Elementfunktionen */
      
      static void * String_constructor(void * _self,const int argnr,\
                                       va_list * app)
      {
        struct String * self = _self;
        const char * text;
      
        if ( app != 0)
          text = va_arg( * app, const char *);
        else
          text="";
        if ( self == 0 )
           fatalerror(NULLPTR,1);
      
        self -> text = (char *) malloc (strlen(text)+1);
        if ( self -> text == 0 )
          fatalerror(ALLOCMEM,1);
        strcpy (self -> text, text);
        return self;
      }

Der Konstruktor der Klasse STRING. Er stellt den Speicher für die Komponente .text zur Verfügung und kopiert den Initialisierungsstring dorthin, sofern app != 0.

      
      static void * String_destructor(void * _self)
      {
        struct String * self = _self;
        if ( self == 0 )
           return;
        free(self -> text);
        self -> text = 0;
        return self;
      }
      
      
      static void * String_copy(const void * _self)
      {
        const struct String * self = _self;
        if (self == 0)
          fatalerror(NULLPTR,1);
        if ( self -> class != STRING)
          fatalerror(TYPE,1);
        return new( STRING ,1, self -> text );
      }
      
      
      
      static void String_cout(const void * _self)
      {
        const struct String * self = _self;
        if (self == 0)
          fatalerror(NULLPTR,1);
        if ( self -> class != STRING)
          fatalerror(TYPE,1);
        fprintf(stdout,"%s", self -> text);
      }
      
      static void String_setvalue(void * _self , char * chars)
      {
        struct String * self = _self;
        char * text;
      
        if ( chars == 0 ) 
          return;
        if ( self == 0 )
          fatalerror(NULLPTR,1);
        if ( self -> class != STRING)
          fatalerror(TYPE,1);
          
        text = (char *) calloc (strlen(chars)+1,sizeof(char));
        if ( text == 0 )
          fatalerror(ALLOCMEM,1);
      
        strcpy ( text, chars);
        free( self -> text );
        self -> text = text;
      }
      
      
      static char * String_getvalue(void * _self )
      {
        const struct String * self = _self;
        char * text;
        if ( self == 0 )
          fatalerror(NULLPTR,1);
        if ( self -> class != STRING)
          fatalerror(TYPE,1);
      
        text = (char *) calloc (strlen(self -> text)+1, sizeof(char));
        if ( text == 0 )
          fatalerror(ALLOCMEM,1);
      
        strcpy(text, self -> text );
        return text;
      }
      
      
      static void String_setindex(void * _self ,int  _index,const char _c)
      {
        struct String * self = _self;
        if ( self == 0 ) 
           fatalerror(NULLPTR,1);
        if ( _index >= strlen(self -> text) )
           fatalerror("index overflow\n",1);
        if ( self -> class != STRING)
          fatalerror(TYPE,1);
      
        (self -> text) [ _index ] = _c;
      }
      
      static char String_getindex(void * _self ,int _index)
      {
        const struct String * self = _self;
        char c;
        if ( self == 0 ) 
           fatalerror(NULLPTR,1);
        if ( _index >= strlen(self -> text) )
           fatalerror("index overflow\n",1);
        if ( self -> class != STRING)
          fatalerror(TYPE,1);
      
        c = (self -> text) [ _index ];
        return c;
      }

Die Elementfunktionen unterscheiden sich nicht besonders von den Funktionen aus dem letzten Kapitel. Interessant ist hier eigentlich nur der explizite Typ-cast: unser Objekt ist vom Typ STRING, und wir müssen also wieder casten, um _self dereferenzieren zu können.
Nun benötigen wir noch die Funktionen, die uns von außen den Zugriff auf die Daten der Klasse geben. Die Grundidee bei der Implementation besteht darin, nicht direkt auf die Daten zuzugreifen, sondern über die Elementfunktionen der Klasse, die wir oben entworfen haben. Das wird uns später bei der Vererbung nützlich sein. (Man nennt das auch dynamische Bindung von Funktionen.)

      
      /* Realisierung der exportierten Funktionen */
      
      
      void setvalue( void * _self, char * chars)
      {
        const struct Class_Str ** self = _self;
        if (self == 0)
          fatalerror(NULLPTR,1);
      
        (*self) -> setvalue(_self, (void *) chars);
      }
      
      char * getvalue (void * _self)
      {
        const struct Class_Str ** self = _self;
        char * text;
        if (self == 0)
          fatalerror(NULLPTR,1);
      
        text = (*self) -> getvalue(_self);
        return text;
      }
      
      void setindex ( void * _self , int i, const char c)
      {
        const struct Class_Str ** self = _self;
        if (self == 0)
          fatalerror(NULLPTR,1);
      
        (*self) -> setindex(_self,i,c);
      }
      
      char getindex ( void * _self, int i)
      {
        const struct Class_Str ** self = _self;
        char c;
        if (self == 0)
          fatalerror(NULLPTR,1);
      
        c = (*self) -> getindex(_self,i);
        return c;
      }

Ein Testlauf:

cn@cicero:/home/cn/seminar/aktuell/kap2 > ls
class.d	    complex.c     kap2.tar      new.c       str.h
compl.c	    error.c	      main.c        new.h       typescript
compl.h	    error.h	      makefile      str.c
cn@cicero:/home/cn/seminar/aktuell/kap2 > make main
gcc -c main.c
gcc -c str.c
gcc -c new.c
gcc -c error.c
gcc -o main main.o str.o new.o error.o
cn@cicero:/home/cn/seminar/aktuell/kap2 > main
Hallo =s
 =t
nochmal Hallo =neues s
Hallo =u
Xallo u nach setindex
Index 4 von u: o
cn@cicero:/home/cn/seminar/aktuell/kap2 >


next up previous contents
Next: Die Klasse COMPLEX Up: KlassenKonstrukturen und Destrukturen: Previous: Die Abstraktion - Loslösung

Christian Neher