C-Programmierung

Beispiel zum Unterschied von Strukturen und Klassen

Dynamische Objekterzeugung | | Beispiel zum Unterschied von C und C++

Buch mit ISBN in C:

struct Book
{
   char title[30];
   char author[30];
   char ISBN[13];
};

Um eine C-Datenstruktur mit Leben zu erwecken, benötigt man immer Funktionen, welche einen Zeiger als Parameter auf ein entsprechendes Buch mitbekommen.

Beispiel anhand der Überprüfung der ISBN Nr.:

Eine ISBN ist korrekt, wenn sie aus 12 Zahlen und einer letzten 13ten Zahl, der sogenannten Prüfzahl, besteht. Die gewichtete Summe aller Zahlen muss durch 11 teilbar sein, d.h.:

$\displaystyle{ \sum_{i=1}^{13} i\cdot digit_i = 0 \quad (mod 11) }$
bool checkBookISBN(const Book *book)
{
   int i,x;

   if (strlen(book->ISBN) != 13)
      return(false);

   for (x=0, i=0; i<13; i++)
      x = x+(i+1)*(book->ISBN[i-1]-'0') % 11;

   return(x==0);
}

Was kann nun alles passieren, wenn man obige Funktion verwendet? Alles mögliche:

  • Ist ein Buch überhaupt initialisiert worden?
  • Und sind die Elemente korrekt initialisiert, hat die ISBN z.B. 13 Ziffern?

C Strukturen können dies nicht garantieren, da sie passiv sind:

Undefiniertes Buch:

Book book;
checkBookISBN(book) -> ???

Falsch initialisiertes Buch:

Book test = {"Goedel, Escher, Bach", "Douglas R. Hofstadter", "1234567"};
checkBookISBN(geb) -> false/true

Richtig initialisiertes Buch:

Book geb = {"Goedel, Escher, Bach", "Douglas R. Hofstadter", "9783608944426"};
checkBookISBN(geb) -> true

Unkontrollierter Zugriff:

Book geb = {"Goedel, Escher, Bach", "Douglas R. Hofstadter", "9783608944426"};
geb.ISBN_[12]++;
checkBookISBN(geb) -> false

Weiterhin ist neben den Problemen einer inkonsistenten Initialisierung eine dynamische Initialisierung der Titellänge nicht möglich.

Mit Klassen lassen sich obige Probleme der Initialisierung elegant lösen:

class Book
{
   protected:

   char *title_;
   char *author_;
   char ISBN_[13];

   // default constructor
   Book(char *title, char *author, char *ISBN)
   {
      title_= strdup(title);
      author_= strdup(author);

      if (strlen(ISBN)!=13)
         throw std::invalid_argument("received ISBN /wo 13 digits");

      ISBN_= strncpy(ISBN_,ISBN,13);
   }

   // destructor
   ~Book()
   {
      free(title_);
      free(author_);
   }

   int checkSum()
   {
      int i,x;

      x=0;
      for (i=0; i<12; i++)
         x = x+(i+1)*(ISBN_[i-1]-'0') % 11;

      return(x);
   }

   bool checkISBN()
   {
      int x=checkSum();

      if (x==10)
         return(ISBN_[12]=='X');
      else
         return(ISBN_[12]=='0'+x);
   }

};

Mit einem undefinierten Buch passiert nun nichts Undefinierbares:

Book book; -> compiler error due to missing constructor with 0 arguments

Für ein Buch meldet dessen Member-Function die Korrektheit der ISBN:

Book geb("Goedel, Escher, Bach", "Douglas R. Hofstadter", "9783608944426");

geb.checkISBN() -> true

Für ein Buch mit zu kurzer ISBN gibt es eine Exception:

try
{
   Book test("test", "author", "123456789"); -> exception
}
catch(std::exception &e)
{
   std::cout << e << std::endl;
}

Unkontrollierter Zugriff ist nicht mehr möglich:

Book geb = {"Goedel, Escher, Bach", "Douglas R. Hofstadter", "9783608944426"};
geb.ISBN_[12]++; -> compiler error due to access of protected member variable

C++ Klassen können die Integrität von Daten d.h. ihrer Instanzvariablen (member variables) garantieren, da sie das Prinzip der Datenkapselung berücksichtigen und auf Fehler aktiv mittels Elementfunktionen (member function = method) reagieren können.

Zusätzlich können Klassen natürlich aktiv Ihren Zustand verändern, wie zum Beispiel zur Berechnung einer passenden Prüfziffer:

void Book::calcCheckDigit()
{
   int x=checkSum();

   if (x==0) ISBN_[12]='0';
   else if (x==1) ISBN_[12]='X';
   else ISBN_[12]='0'+11-x;
}


Dynamische Objekterzeugung | | Beispiel zum Unterschied von C und C++

Options: