Einleitung
Im Einführungskapitel wird zunächst die Notwendigkeit von Shared Pointern verdeutlicht
und zu deren
Verwendung motiviert. Nicht ohne Grund sind Shared Pointer bei einigen höheren
Programmiersprachen wie z.B.
das ARC (Automatic Reference Counting) bei
Objective-C
ein Standard Sprach-Feature der Programmiersprache.
Lesern, die bereits Grundkenntnisse besitzen und sich für ein lauffähiges Codesnippet
interessieren,
empfehlen wir, direkt bei Kapitel 2 Grundlagen der shared_ptr einzusteigen. Darin wird
anhand von Beispielen
die Verwendung der Shared Pointer erklärt.
Einführung - Das Mysterium der Shared Pointer in C++
“Warum kann man nicht einfach Shared Pointer verwenden? Shared Pointer sind eine
sehr schlaue
Methode zur Verwaltung von Pointern und des Speichers.”
Diese Aussage hört man sehr häufig. Aber werden Shared Pointer bereits wirklich im
größeren Stil und
konsequent in Projekten eingesetzt? Im Austausch mit Entwicklern gewinnt man schnell den
Eindruck, dass
Shared Pointer nicht besonders weit verbreitet oder populär sind. Woran liegt das? Gibt
es Probleme, die bei
der Verwendung häufiger auftreten? Sind Shared Pointer zu kompliziert zu verstehen oder
ist es zu aufwendig,
diese zu benutzen?
Wir hoffen, dass dieser Beitrag dabei helfen wird, einige der Fragen um das Mysterium
Shared Pointer zu
klären und ein gutes Beispiel dafür geben zu können, wie man diese korrekt
verwendet.
Einführung - Eine Analogie zu Shared Pointern
Anstatt direkt in technische Details zu springen, wird die Verwendung von Shared Pointern
zunächst anhand
eines Beispiels aus dem echten Leben erläutert:
Stellen Sie sich vor, Sie arbeiten in einer Firma und es gibt eine einfache Regelung für
das Bürogebäude: der
letzte Mitarbeiter, welcher das Gebäude verlässt, muss die Eingangstür zum Gebäude
verriegeln und die
Alarmanlage aktivieren. Falls jemand das Gebäude verlässt und die Alarmanlage ist
bereits aktiviert, dann
wird der Alarm ausgelöst. Deswegen sollten Sie hier genau prüfen, ob sich noch jemand
anderes im Gebäude
befindet, bevor Sie die Alarmanlage aktiv schalten.
Angenommen, das Bürogebäude besteht nur aus einem kleinen Zimmer und es gibt nur fünf
Mitarbeiter, dann ist
es für Sie sehr einfach festzustellen, ob Sie der letzte Mitarbeiter sind, der sich noch
im Gebäude
befindet. Stellen Sie sich jedoch vor, Sie arbeiten in einem riesigen Bürogebäude mit
mehreren Stockwerken
und vielen hunderten Mitarbeitern. Um hier noch zu wissen, ob Sie die letzte Person im
Gebäude sind, müssten
Sie alle Stockwerke und alle Zimmer absuchen. Nun überlegen Sie sich, jemand anderes
sucht zur gleichen Zeit
das Gebäude ab, um nachzusehen, ob noch jemand anderes im Gebäude ist und sie beide
verfehlen sich dadurch.
Am Ende schließen Sie aus Versehen die andere Person im Gebäude ein, mit aktivierter
Alarmanlage. Was für
ein Chaos!
Aber was wäre, wenn es eine Art “Smart Door”, also eine Art “schlaue Eingangstür” zum
Gebäude gäbe, welche +1
für jede Person addiert, die das Gebäude betritt und -1 subtrahiert für jede Person, die
das Gebäude
verlässt. Sobald der Zählerstand also den Wert 0 erreicht, wird die Alarmanlage
automatisch aktiviert.
Wäre das nicht eine großartige Lösung für dieses Problem? Die Idee, welche sich hinter
den Shared Pointern
verbirgt, ähnelt der “Smart Door” aus dem erläuterten Beispiel.
Einführung - Die Verwendung von C++ shared_ptr
Bei der Programmierung in C++ ist es oft nicht einfach, den Überblick über die
Speicherverwaltung des
gesamten Source Codes eines Projektes zu behalten. Sobald ein C++ Pointer erzeugt wurde,
kommen häufig
folgende Fragen auf:
- Wann soll der Pointer wieder gelöscht werden?
- Was, wenn ein anderer den Pointer noch benötigt?
- Und wenn der Pointer nicht gelöscht wird, wissen andere Beteiligte dann, ob und wann
der Pointer
gelöscht werden soll?
Als simple Antwort auf diese Fragen folgt dann meist folgende Aussage:
“Der Pointer wird an der Stelle im Programm gelöscht, in der er als letztes
verwendet wurde.”
Aber wie in dem Beispiel aus 1.2, in dem nicht eindeutig bestimmt werden kann, wieviele
Leute sich in einem
großen Gebäude befinden, und wo genau diese sich aufhalten, so ist es keineswegs
einfach, den Überblick
darüber
zu behalten, wohin alle Pointer in einem Programm weitergereicht wurden.
"Woher weiß man, welches die letzte Stelle ist, an der ein Pointer verwendet
wurde?"
Ein Pointer könnte unter Umständen durch mehrere Konstruktoren oder Methoden Parameter
weitergereicht worden
sein. Außerdem könnte es sein, dass man mit mehreren Teamkollegen zusammen am selben
Source Code
arbeitet.
Vergleicht man die Behandlung von Pointern in C++ mit dem Beispiel aus 1.2, dann wäre ein
Aufruf des delete
Befehls auf einen Pointer, für welchen der Speicher bereits freigegeben wurde,
gleichbedeutend mit dem
Versuch, das Gebäude zu verlassen, in welchem bereits die Alarmanlage aktiviert wurde.
Sobald man versucht,
die Tür von innen zu öffnen, würde die Alarmanlage ausgelöst und bei den Pointern das
Programm
abstürzen.
Welches die letzte Stelle ist, an der ein Pointer verwendet wurde und zu wissen zu
welchem Zeitpunkt man
delete aufrufen sollte, ist keine Frage die sich so einfach beantworten lässt, sofern es
eine dritte Instanz
gibt, welche sich um diese Aufgabe kümmert. Und mit dieser dritten Instanz kommen Shared
Pointer, wie der std::shared_ptr
ins Spiel.
Der Shared Pointer weiß genau, welches die letzte Stelle ist, an der ein Pointer
verwendet wurde und sobald
ein Pointer zum letzten Mal im gesamten Programm verwendet wurde, wird er automatisch
deleted. Hierfür
addiert er +1. Dies geschieht jedes Mal, wenn jemand eine Kopie erzeugt, also z.B. wenn
der Shared Pointer
in einer Methode oder in einem Konstruktor als Parameter übergeben wird. Des Weiteren
wird immer -1
subtrahiert, wenn das Objekt, welches den Shared Pointer verwendet hat, gelöscht oder
die Methode, welche
den Shared Pointer verwendet hat, beendet wurde. Sobald der Wert 0 erreicht wird, wird
der Pointer
automatisch gelöscht und der belegte Speicher wieder freigegeben.
Grundlagen der shared_ptr
Nachfolgend werden nun einige Code Beispiele präsentiert. Aufgrund der Anschaulichkeit
wird auch hier nochmal
das Büro Beispiel aus Kapitel 1.2 aufgegriffen:
Es wird zunächst eine Klasse OfficeBuilding definiert, also ein Gebäude, welches von
Mitarbeitern betreten
werden kann. Das bedeutet, ein OfficeBuilding kann mehrere Instanzen eines Mitarbeiter
Objektes (Employee)
referenzieren. Und als kleines, zusätzliches Feature gibt es in dem Gebäude ein WC
(Washroom), welches von
genau einem Mitarbeiter betreten werden kann.
Grundlagen der shared_ptr - Instanziierung eines shared_ptr
Möchte man z.B. eine Instanz eines Objektes Employee erzeugen, dann würde man
normalerweise einen Pointer mit
Hilfe des Schlüsselworts new instanziieren, um Heap-Speicher zu allokieren. Das
Schlüsselwort delete würde
man dazu verwenden, um den Speicher wieder freizugeben:
Employee *employee1 = new Employee( 1, “Peter Parker”);
delete employee1;
Diese beiden Zeilen Code werden nun ersetzt durch die Folgende:
shared_ptr<Employee> employee1 = make_shared<Employee>(1, "Peter Parker");
Die linke Seite der Zuweisung shared_ptr<Employee>
employee1
hält
sich eine Instanz der
Klasse Employee und anstatt das Schlüsselwort new zu verwenden, verwendet man das
Schlüsselwort make_shared,
um diese Instanz zu erzeugen.
Die Parameter, welche man normalerweise dem Konstruktor der Klasse Employee übergeben
würde, werden
stattdessen dem Ausdruck shared_ptr<Employee>
(parameter1, parameter2,
...)
übergeben.
Sobald der aktuelle Kontext verlassen wird, also z.B. der Kontext der momentan
aufgerufenen Methode, wird der
Speicher, welcher von der employee1
Instanz belegt
wurde, automatisch
freigegeben. Der Aufruf
des delete-Befehls ist somit nicht notwendig.
Grundlagen der shared_ptr - Zugriff auf Class Members eines Shared Pointers
Angenommen, man möchte unter Verwendung der Getter Methode auf den Namen der Variablen
employee1
zugreifen, dann könnte man dies tun, indem man die Variable wie einen ganz normalen C++
Pointer behandelt
und den Pfeil Operator “->” verwendet:
string employeeName = employee1->getName();
Die Verwendung des Punkt Operators “.” gewährt im Gegensatz dazu den Zugriff auf
hilfreiche Informationen
über die shared reference wie zum Beispiel die Anzahl an Reference Counts:
long employee1UseCount = employee1.use_count();
Theoretisch betrachtet, ist es möglich, über den Shared Pointer auch noch auf den Raw
Pointer zuzugreifen,
welcher vom Shared Pointer verwaltet wird:
Employee *pointer = employee1.get();
Jedoch sollte ein Zugriff auf den Raw Pointer, wie in diesem Beispiel um jeden Preis
vermieden werden, weil
dies wieder nur zu altbekannten Problemen führt: jemand versucht, Speicher freizugeben,
welcher bereits
freigegeben wurde. Diese Möglichkeit soll hier nur aus Gründen der Vollständigkeit
erwähnt werden.
Grundlagen der shared_ptr - Kopieren eines shared_ptr
Wie kann man eine Kopie eines shared_ptr machen, welcher auf allokierten Speicher
verweist? Vergleicht man es
mit normalen Pointern, dann würde man normalerweise Folgendes schreiben:
Employee *employee1 = new Employee( 1, “Peter Parker”);
Employee *employee1Copy = employee1;
Die erste Zeile des Codes allokiert ein Employee Objekt und lässt den Pointer employee1
auf den
neu allokierten Speicher zeigen. Anschließend wird in der zweiten Zeile eine Kopie
dieser Variable gemacht
und die neue Variable employee1Copy
verweist dann auf
denselben
Speicherbereich wie employee1
.
Wenn man etwas vergleichbares mit einem shared_ptr machen möchte, dann gibt es zwei
Möglichkeiten:
shared_ptr<Employee> employee1 = make_shared<Employee>(1, "Peter Parker");
1) shared_ptr<Employee> employee1Copy = employee1;
2) auto employee1Copy2 = employee1;
Wie verhält sich das Reference Counting?
Nachdem nun die Grundlagen über shared_ptr vermittelt wurden, wäre es als nächstes
interessant, zu
betrachten, wie sich das Reference Counting in einem richtigen Code Beispiel
verhält.
Zunächst soll die Klasse Employee betrachtet werden. Ein Mitarbeiter (Employee) hat ein
Attribut
Name
und eine eindeutige Id
, welche dem
Konstruktor übergeben werden kann. Diese
Klasse enthält, um das Beispiel möglichst einfach zu halten, keine Pointer oder Shared
Pointer.
#ifndef SHAREDPOINTEREXAMPLEAPPLICATION_EMPLOYEE_H
#define SHAREDPOINTEREXAMPLEAPPLICATION_EMPLOYEE_H
#import <vector>
#import <string>
#import <iostream>
using namespace std;
class Employee {
private:
int id;
string name;
public:
Employee(int id, const string &name) : id(id), name(name){
}
int getId() const{
return id;
}
const string &getName() const{
return name;
}
void printCurrentUseCount(shared_ptr<Employee> employee){
if(employee.use_count() > 0){
printf("%s, use count: %li n", getName().c_str(), (employee.use_count() -1));
} else{
printf("use count: %li n", employee.use_count());
}
}
};
#endif //SHAREDPOINTEREXAMPLEAPPLICATION_EMPLOYEE_H
Um einen besseren Überblick über die Reference Counts zu haben, gibt es noch eine Methode
printCurrentUseCount
.
Da bei jedem Aufruf der Methode ein neuer Kontext betreten wird und damit das Ergebnis
um den Wert +1
“verfälscht” würde, wird hier wieder das auszugebende Ergebnis immer um -1
heruntergesetzt.
Das OfficeBuilding, in welchem die Mitarbeiter arbeiten, kann durch den Aufruf der Method
enterBuilding und
leaveBuilding von Mitarbeitern betreten bzw. verlassen werden. Bei genauerem Hinsehen
fällt auf, dass die
Klasse OfficeBuilding eine Klassenvariable vector<shared_ptr<Employee>>
smartDoorPeopleCounter
hat, welche sich merkt, welche Mitarbeiter sich zur
Zeit im Gebäude
befinden.
Über die beiden Methoden useWashRoom und leaveWashRoom kann immer genau ein Mitarbeiter
das WC betreten bzw.
wieder verlassen.
#ifndef SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEBUILDING_H
#define SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEBUILDING_H
#include "Employee.h"
#include "WashRoom.h"
#import <vector>
#import <string>
#include <thread>
#include <cstdlib>
class OfficeBuilding {
private:
vector<shared_ptr<Employee>> smartDoorPeopleCounter;
vector<WashRoom> washRoom;
public:
void enterBuilding(shared_ptr<Employee> employeeWhoWantsToEnter) {
smartDoorPeopleCounter.push_back(employeeWhoWantsToEnter);
employeeWhoWantsToEnter->printCurrentUseCount(employeeWhoWantsToEnter);
}
void leaveBuilding(shared_ptr<Employee> employeeWhoWantsToLeave) {
int indexToRemove = -1;
for (int i = 0; i < smartDoorPeopleCounter.size(); i++) {
auto currentEmployee = smartDoorPeopleCounter[i];
if (currentEmployee->getId() == employeeWhoWantsToLeave->getId()) {
indexToRemove = i;
}
}
if (indexToRemove != -1) {
smartDoorPeopleCounter.erase(smartDoorPeopleCounter.begin() + indexToRemove);
}
if (smartDoorPeopleCounter.size() == 0) {
// activate alarm system
}
}
void useWashRoom(shared_ptr<Employee> employee) {
if (washRoom.size() == 0) {
WashRoom emptyWashRoom;
emptyWashRoom.setEmployee(employee);
washRoom.push_back(emptyWashRoom);
} else {
//washroom is currently in use, please wait!
}
}
void leaveWashRoom() {
if (washRoom.size() == 1) {
washRoom.clear();
}
}
};
#endif //SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEBUILDING_H
Das Objekt Washroom hält sich immer einen shared_ptr auf den aktuellen Mitarbeiter,
welcher gerade das WC
benutzt.
#ifndef SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEROOM_H
#define SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEROOM_H
#include "Employee.h"
class WashRoom {
shared_ptr<Employee> employee;
public:
void setEmployee(const shared_ptr<Employee> &employee) {
WashRoom::employee = employee;
}
const shared_ptr<Employee> &getEmployee() const {
return employee;
}
};
#endif //SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEROOM_H
Der folgende Beispielcode allokiert einige Mitarbeiter Objekte (Employee) und lässt diese
das OfficeBuilding
betreten und wieder verlassen.
OfficeBuilding officeBuilding;
/* create employee 1 */
shared_ptr<Employee> employee1 = make_shared<Employee>(1, "Peter Parker");
employee1->printCurrentUseCount(employee1); //Peter Parker, use count: 1
string employeeName = employee1->getName(); // "Peter Parker"
Employee *pointer = employee1.get(); // attention: raw pointer should not be used!!
shared_ptr<Employee> employee1Copy = employee1;
employee1->printCurrentUseCount(employee1); //Peter Parker, use count: 2
auto employee1Copy2 = employee1;
employee1->printCurrentUseCount(employee1); //Peter Parker, use count: 3
/* create employee 2 */
shared_ptr<Employee> employee2;
employee2->printCurrentUseCount(employee2); //use count: 0
employee2 = make_shared<Employee>(2, "Bruce Wayne");
employee2->printCurrentUseCount(employee2); //Bruce Wayne, use count: 1
/* create employee 3 */
auto employee3 = make_shared<Employee>(3, "Clark Kent");
employee3->printCurrentUseCount(employee3); //Clark Kent, use count: 1
/* employees entering building */
officeBuilding.enterBuilding(employee1); //Peter Parker, use count: 5
employee1->printCurrentUseCount(employee1); //Peter Parker, use count: 4
officeBuilding.enterBuilding(employee2); //Bruce Wayne, use count: 3
employee2->printCurrentUseCount(employee2); //Bruce Wayne, use count: 2
officeBuilding.enterBuilding(employee3);//Clark Kent, use count: 3
employee3->printCurrentUseCount(employee3);//Clark Kent, use count: 2
/* employee 1 entering using washroom */
officeBuilding.useWashRoom(employee1);
employee1->printCurrentUseCount(employee1); //Peter Parker, use count: 5
officeBuilding.leaveWashRoom();
employee1->printCurrentUseCount(employee1);//Peter Parker, use count: 4
/* employee 1 leaving building */
officeBuilding.leaveBuilding(employee1);
employee1->printCurrentUseCount(employee1);//Peter Parker, use count: 3
/* employee 1 visiting coffee house */
CoffeeHouse coffeeHouse;
coffeeHouse.visitCoffeeHouse(employee1);
employee1->printCurrentUseCount(employee1);//Peter Parker, use count: 4
bool finish = true;
Die Print-Ausgaben sollen hier verdeutlichen, wie jeweils der aktuelle Reference Count
ist:
Peter Parker, use count: 1
Peter Parker, use count: 2
Peter Parker, use count: 3
use count: 0
Bruce Wayne, use count: 1
Clark Kent, use count: 1
Peter Parker, use count: 5
Peter Parker, use count: 4
Bruce Wayne, use count: 3
Bruce Wayne, use count: 2
Clark Kent, use count: 3
Clark Kent, use count: 2
Peter Parker, use count: 5
Peter Parker, use count: 4
Peter Parker, use count: 3
use count inside context: 5
use count outside context: 4
use count from thread: 6
use count after thread finished : 5
Peter Parker, use count: 4
Die Verwendung von shared_ptr innerhalb von Threads
Es wird vermutet, dass es bei der Verwendung von C++ Shared Pointern zu Problemen im
Zusammenhang mit Threads
kommen könnte. Nachfolgend auch ein Beispiel für diesen speziellen Fall: es gibt ein
Cafe, welches sich
außerhalb des Gebäudes OfficeBuilding befindet und welches ein Mitarbeiter (Employee)
besuchen kann.
Das Cafe kann mittels der Methode visitCoffeeHouse
betreten werden. In
dieser Methode wird
zunächst der Shared Pointer auf den Mitarbeiter einer auto Variable zugewiesen und
anschließend einem Shared
Pointer struct, welcher als Container für die Parameter des Threads dient.
#ifndef SHAREDPOINTEREXAMPLEAPPLICATION_COFEEHOUSE_H
#define SHAREDPOINTEREXAMPLEAPPLICATION_COFEEHOUSE_H
#include "Employee.h"
#import <vector>
#import <string>
#include <thread>
#include <cstdlib>
#include <unistd.h>
using namespace std;
static pthread_t threadID = pthread_t();
class CoffeeHouse {
private:
shared_ptr<vector<shared_ptr<Employee>>> cafeteriaCounter;
struct threadParams {
shared_ptr<Employee> employeeCopy;
shared_ptr<vector<shared_ptr<Employee>>> cafeteriaCounterCopy;
};
static void *visitCoffeeHouseAsync(void *context) {
unsigned int microseconds = 5000000; // sleep for 5 seconds
usleep(microseconds);
auto params = *(shared_ptr<threadParams> *) context;
auto employee = params->employeeCopy;
string employeeName = employee->getName();
if (employeeName.length() > 0) {
params->cafeteriaCounterCopy->push_back(employee);
printf("use count from thread: %li n", (employee.use_count()));
}
pthread_detach(pthread_self());
return nullptr;
}
public:
CoffeeHouse(){
cafeteriaCounter = make_shared<vector<shared_ptr<Employee>>>();
}
void visitCoffeeHouse(shared_ptr<Employee> employee) {
{
shared_ptr<threadParams> params = make_shared<threadParams>();
params->employeeCopy = employee;
params->cafeteriaCounterCopy = cafeteriaCounter;
printf("use count inside context: %li n", (employee.use_count())); //use count inside context: 5
pthread_create(&threadID, NULL, visitCoffeeHouseAsync, ¶ms);
}
printf("use count outside context: %li n", (employee.use_count())); //use count outside context: 4
pthread_join(threadID, NULL);
printf("use count after thread finished : %li n", (employee.use_count()));//use count after thread finished : 5
}
};
#endif //SHAREDPOINTEREXAMPLEAPPLICATION_COFEEHOUSE_H
Der Thread wird innerhalb eines speziellen Kontextes gestartet, wobei der Kontext durch
die geschweiften
Klammern { } markiert ist. Innerhalb des Kontextes wird der useCount
den
Wert 5 haben, kurz
bevor der Thread gestartet wird. Nachdem der Thread gestartet wurde, wird der Kontext
verlassen, während der
Thread für 5 Sekunden schläft und in der Zwischenzeit wird der Reference Count durch den
Main Thread auf den
Wert 4 verringert.
Sobald der Thread wieder erwacht, wird dieser eine lokale und eine globale Kopie des
Shared Pointer machen,
was zu der Ausgabe des Wertes 6 führt.
Nachdem der Thread beendet wurde, verbleibt lediglich die globale Kopie, was zu einer
Ausgabe des Wertes 5
führt. Das Endergebnis nach dem Verlassen der Methode visitCoffeeHouse für den
Mitarbeiter “Peter Parker”
ist der Wert 4.
Auf den ersten Blick sieht mit den Threads alles relativ normal aus. Das heißt, die
reference counts scheinen
korrekt erhöht und verringert zu werden. Jedoch können Probleme nie völlig
ausgeschlossen werden. Möchte man
ganz sicher gehen, empfiehlt es sich, nur auf einer Kopie der Daten zu arbeiten.
Zusammenfassung
Das Konzept der C++ shared_ptr bietet eine Alternative zu den traditionellen C++
Pointern. Es ist jedoch
entscheidend, niemals normale C++ Pointer und Shared Pointer zu vermischen, weil dies zu
ernsthaften
Problemen führen kann.
Der große Vorteil der Shared Pointer, die automatische Verwaltung von
Speicher-Referenzen, ersetzt außerdem
die mühselige, manuelle Speicherfreigabe. Des Weiteren wurde ein Einblick gegeben, wie
sich das Reference
Counting verhält. Einfach gesagt: Wann immer ein neuer Kontext betreten wird z.B. eine
Methode oder eine
Kopie eines Shared Pointers, wird der Reference Count um +1 erhöht. Sobald ein Kontext
wieder verlassen
wird, wird der Reference Count automatisch verringert, abhängig von der Anzahl der
Kopien in diesem Kontext.
Nach intensiver Einarbeitung in die Thematik, lässt sich festhalten, dass Shared Pointer
den
Entwicklungsprozess deutlich komfortabler und einfacher machen können. Auch die
Kollaboration mit
Teamkollegen wird durch diese deutlich erleichtert. Es dürfte spannend werden, Shared
Pointer zukünftig auch
in größeren Projekten einzusetzen.
Einen kleinen Nachteil könnte es dabei jedoch geben: sofern man Shared Pointer in einem
Projekt verwendet und
im selben Projekt auch eine externe Bibliothek wie z.B. OpenCV verwenden möchte, welche
vielleicht in den
Interfaces keine Shared Pointer anbietet, sollte man sehr vorsichtig damit umgehen.
Möglicherweise bietet es
sich an, hier dann eine “tiefe Kopie” der Daten zu verwenden, welche man als Parameter
den Schnittstellen
der Bibliothek übergeben möchte.