C++ Kurs

Inline (Member-)Funktionen

Die Themen:

Einführung
inline-Funktionen
inline-Memberfunktionen
Einschränkungen bei inline

Einführung

In dieser Lektion werden wir einmal etwas für die Laufzeit-Optimierung einer Anwendung tun.

Um auf die geschützten Eigenschaften einer Klasse zugreifen zu können, muss ja eine entsprechende Memberfunktion aufgerufen werden. Bei kurzen Memberfunktionen, die z.B. nur eine Eigenschaft setzen oder zurückliefern, wird dabei aber mehr Zeit für den Aufruf benötigt als für das Setzen oder Lesen der Eigenschaft. Nachfolgend sehen Sie einmal die Aktionen, die beim Aufruf einer Memberfunktion durchgeführt werden. Dabei haben die Aktionen 1,2,3,5 und 6 nichts direkt mit der gewünschten Funktionalität zu tun. Man bezeichnet dieses auch als Overhead. Nur die Aktionen 4 und 7 sind eigentlich notwendig um das gewünschte Ergebnis zu erhalten.  Und um diesen Overhead zur vermeiden, wurden die so genannten inline-Funktionen bzw. inline-Memberfunktionen eingeführt.

  1. Parameter auf dem Stack ablegen
  2. Rücksprungadresse auf dem Stack ablegen
  3. Sprung zur Memberfunktion
  4. Memberfunktion ausführen
  5. Rücksprung in die aufrufende Funktion
  6. Parameter vom Stack entfernen
  7. nächste Anweisung ausführen

inline-Funktionen

Sehen wir uns zunächst die inline-Funktionen an. Inline-Funktionen werden prinzipiell genauso definiert wie 'normale' Funktionen. Zusätzlich wird nun aber vor dem Returntyp der Funktion das Schlüsselwort inline gestellt.


inline void CheckError(int err, char* pT)
{
   if (err)
   {
      cout << "Fehler: " << err << pT << endl;
      exit (err);
   }
}

Triff der Compiler dann beim Übersetzen auf den Aufruf einer inline-Funktion, so wird anstelle des Funktionsaufrufs direkt der Code der Funktion eingefügt. Und somit entfällt der komplette Overhead für den Aufruf. Im Beispiel ist als Kommentar dargestellt, wie der Compiler den Funktionsaufruf der inline-Funktion CheckError(...) ersetzt.


// Inline-Funktion definieren
inline void CheckError (int err, char* pT)
{
   ... // Code siehe oben
}

int main()
{
   ...
   CheckError(status,"Überlauf");
   // Wird durch den Compiler erweitert zu:
   // if (status)
   // {
   //    cout << "Fehler << status << "Überlauf" << endl;
   // }

   ...
}

Da nun anstelle des Funktionsaufrufs direkt der Funktionscode ins übersetzte Programm eingefügt wird, empfiehlt es sich, nur kurze Funktionen als inline-Funktionen zu definieren, da ansonsten die Größe des übersetzten Programms unter Umständen beträchtlich anwachsen kann.

inline-Funktionen entsprechen von der Wirkungsweise her in etwa den Präprozessor-Makros (#define-Direktive). Sie können sich jetzt die Präprozessor-Makros hier nochmals ansehen. Der Hauptunterschied zu den Makros liegt darin, dass die Parameter bei inline-Funktionen typisiert sind.

inline-Memberfunktionen

Werden Memberfunktionen innerhalb einer Klasse definiert, so sind sie unter gewissen Einschränkungen (folgen gleich noch) defaultmäßig als inline-Memberfunktion definiert. Beachten Sie dies bitte, wenn Sie Memberfunktionen innerhalb einer Klasse definieren. Sie sollten dies wirklich nur für sehr kurze Memberfunktionen tun.


// Klassendefinition
class Window
{
   string title;
   ...
 public:
   // defaultmässig inline-Memberfunktion!
   const string& GetTitle() const
   {
      return title;
   }
   ...
};

// main() Funktion
int main()
{
   Window myWin;
   ...
   cout << myWin.GetTitle() << endl;
   // wird compiliert zu:
   // cout << myWin.title << endl;
   ...
}

Wird eine Memberfunktion innerhalb einer Klasse nur deklariert, so kann sie bei ihrer Definition außerhalb der Klasse durch voranstellen des Schlüsselworts inline vor dem Returntyp ebenfalls als inline-Memberfunktion definiert werden. Und auch hier gelten ebenfalls Einschränkungen die gleich noch aufgeführt werden.


// Klassendefinition
class Window
{
   string title;
   ...
 public:
   const string& GetTitle() const;
   ...
};

// inline Definition
inline const string& Win::GetTitle() const
{
   return title;
}

Aber Achtung! Sollten Sie Ihre Klasse auf zwei Dateien aufgeteilt haben (Header-Datei mit der Klassendefinition und CPP-Datei mit den Definitionen der Memberfunktionen), so müssen die inline-Memberfunktionen immer mit in die Header-Datei aufgenommen werden, damit der Compiler den einzusetzenden Code kennt.

Einschränkungen bei inline

Wie bereits mehrfach erwähnt, bestehen bei inline-Funktionen bzw. Memberfunktionen gewisse Einschränkungen. So sind z.B. rekursive Funktionen (das sind Funktionen die sich selbst aufrufen) in der Regel nicht als inline-Funktionen zugelassen. Auch schließen einige Compiler bestimmte Anweisungen innerhalb von inline-Funktionen aus. Beim BORLAND-Compiler waren dies fast alle Anweisungen, die irgendwelche Sprünge verursachen wie z.B. Schleifen. Enthält eine inline-Memberfunktion eine nicht zugelassene Anweisung, so wird die Funktion bzw. Memberfunktion nicht als inline betrachtet.

Womit wir auch schon beim nächsten und wichtigsten Punkt wären. Die Definition einer Memberfunktion als inline ist nur eine Bitte an den Compiler, die Funktion/Memberfunktion entsprechend einzubauen und keine zwingende Vorschrift.

Und zum Schluss ist noch anzumerken, dass moderne Compiler versuchen den Code eines Moduls so gut wie möglich zu optimieren. Und dazu gehört teilweise die eigenständige Definition einer Funktion als inline-Funktion wenn sie im gleichen Modul (CPP-Datei) verwendet wird. Aber wie so oft lässt sich auch hierbei sehr viel über entsprechende Einstellungen des Compilers einstellen. Da hilft nur ein Blick ins Handbuch bzw. in die Online-Hilfe.