Open Source im professionellen Einsatz

Modernes C++ in der Praxis – Folge 1

Die Elf spielt auf

Die jüngste Reform der Programmiersprache C++ ist abgeschlossen. Das Linux-Magazin widmet dem neuen Standard C++11 eine Artikelserie, die dem Programmierer zeigt, wie er in der Praxis von den Neuerungen profitiert. Die erste Folge stellt Lambda-Funktionen vor.

Nach langer Arbeit trägt der neue C++-Standard jetzt auch einen Namen: Der bisherigen Konvention folgend heißt er C++11, weil er im Jahr 2011 verabschiedet wurde. Die Standardisierungsbehörde ISO bietet die Spezifikation zum Kauf an, Spracherfinder Bjarne Stroustrup hat den letzten kostenlosen Entwurf unter [1] verlinkt. Der Standard präsentiert sich im stolzen Umfang von gut 1300 Seiten. Da war sein Vorgänger von 2003 mit knapp 700 Seiten deutlich schmaler.

Bedeutender als der Umfang ist der Inhalt des neuen C++11: Er bietet vieles, was den Einstieg erleichtert und den Profi unterstützt. Der Einsteiger profitiert davon, dass der Compiler seine Datentypen automatisch bestimmt, Klassen einfacher zu definieren sind und sich Daten einheitlich initialisieren lassen. Das ist aber noch lange nicht alles: Dem C++-Novizen gibt der neue Standard außerdem mächtige Bibliotheken an die Hand, die Strings verarbeiten, das Speichermanagement von Variablen automatisieren und die CPUs seines Rechners ausreizen.

Das alles erfreut auch den Profi. Doch für ihn beginnt damit das Angebot erst richtig: Er darf sich auf Algorithmen freuen, die sich dank Lambda-Funktionen mit der Leichtigkeit einer Interpreter-Sprache implementieren lassen. Daneben erwarten ihn deutlich leistungsfähigere Werkzeuge zur Template-Programmierung, mit denen er Datenstrukturen für seine Anforderungen maßschneidert.

Wer genauer wissen will, welche Features C++11 zu bieten hat, der sei auf die Artikel des Linux-Magazins zur Erweiterung der Kernsprache [2] und zu den neuen Bibliotheken in C++ [3] verwiesen. Wer es noch genauer nachlesen möchte, greift zu Pete Beckers Buch über die Bibliothekserweiterung [4] oder zum Band über die neue Multithreading-Funktionalität von Anthony Williams [5].

Das bessere C++

Diese Artikelserie soll keinen vollständigen Überblick über den neuen C++-Standard bieten. Das ist Aufgabe der oben erwähnten Bücher. Sie möchte alle zwei Monate nur einzelne Komponenten in der Anwendung zeigen und demonstrieren, dass sich der Umstieg auf die moderne Variante lohnt – nicht nur für den C++-Entwickler, denn die Zeichen verdichten sich, dass die Sprache vor einer Renaissance steht [6].

Als Einstieg dienen in dieser ersten Folge die Lambda-Funktionen. In den folgenden Beispielen kommen sie zum Einsatz, um die Elemente eines »std::vector« zu modifizieren. In der Evolution von den C-Funktionen über die C++-Funktionsobjekte bilden die Lambda-Funktionen in C++11 das letzte Glied der Kette (Abbildung 1).

Abbildung 1: Sprachhistorie: Von den Funktionen in C hat die Entwicklung über die Funktionsobjekte in C++ bis zu den Lambda-Funktionen in C++11 geführt.

Kompakter Code

Um Platz zu sparen, setzt das Listing 1 gleich mehrere C++11-Features ein: Die Standardaufgabe, den »std::vector« zu initialisieren, geht in C++11 aufgrund der neuen Initialisierer-Liste »{1,2,3,4, 5,6,7,8,9,10}« in Zeile 13 leicht von der Hand. Die Range-basierte For-Schleife »for (auto v: myVec1)« in Zeile 16 reduziert das Iterieren über einen Container auf die nötigste Schreibarbeit. Zusätzlich ist die automatische Typableitung mit »auto« im Einsatz, die den Typ »int« für »v« ermittelt.

Listing 1

Funktionen

01 #include <algorithm>
02 #include <iomanip>
03 #include <iostream>
04 #include <vector>
05
06 void add3(int& i){
07   i +=3;
08 }
09
10 int main(){
11   std::cout << std::endl;
12
13   std::vector<int> myVec1{1,2,3,4,5,6,7,8,9,10};
14   std::cout << std::setw(20) << std::left << "myVec1: i->i+3:     ";
15   std::for_each(myVec1.begin(),myVec1.end(),add3);
16   for (auto v: myVec1) std::cout << std::setw(6) << std::left << v;
17
18   std::cout << "\n\n";
19 }

Die eigentliche Aufgabe, das Modifizieren des Vektors, löst Zeile 15 in klassischer C-Manier. Um jedes Element eines »std::vector« um 3 zu erhöhen, bietet sich in der Standard Template Library (STL) der Algorithmus »std::for_each« in Kombination mit der Funktion »add3« in Zeile 15 an. Da jedes Element des Vektors verändert wird, muss das Argument von »add3« als Referenz in »std::for_each« adressiert sein. Abbildung 2 zeigt das Ausführen des Programms in einem Terminalfenster.

Abbildung 2: Listing 1 – auf der Kommadozeile ausgeführt – verändert die Elemente eines Vektors mittels einer Funktion.

Soll das Programm jedoch jedes Element um einen beliebigen Wert verändern, endet die Flexibilität einer Funktion – das Mittel der Wahl in C++ heißt Funktionsobjekt. Die Zeilen 20 und 27 in Listing 2 erzeugen ein Funktionsobjekt, das anschließend die Elemente des Containers modifiziert. Kommt ein Funktionsobjekt zum Einsatz, wird dessen überladener Klammeroperator aus Zeile 9 angewandt. In Abbildung 3 ist die Ausgabe des Programms zu sehen.

Listing 2

Funktionsobjekte

01 #include <algorithm>
02 #include <iomanip>
03 #includefff <iostream>
04 #include <vector>
05
06 class AddN{
07 public:
08   AddN(int n):num(n){};
09   void operator()(int& i){
10     i +=num;
11   }
12 private:
13   int num;
14 };
15
16 int main(){
17   std::cout << std::endl;
18
19   std::vector<int> myVec2{1,2,3,4,5,6,7,8,9,10};
20   AddN add4(4);
21   std::for_each(myVec2.begin(),myVec2.end(),add4);
22   std::cout << std::setw(20) << std::left << "myVec2: i->i+4:     ";
23   for (auto v: myVec2) std::cout << std::setw(6) << std::left << v;
24   std::cout << "\n";
25
26   std::vector<int> myVec3{1,2,3,4,5,6,7,8,9,10};
27   AddN addMinus5(-5);
28   std::for_each(myVec3.begin(),myVec3.end(),addMinus5);
29   std::cout << std::setw(20) << std::left << "myVec3: i->i-5:     ";
30   for (auto v: myVec3) std::cout << std::setw(6) << std::left << v;
31
32   std::cout << "\n\n";
33 }
Abbildung 3: Die Elemente des Vektors lassen sich auch durch die typische C++-Vorgehensweise mit Funktionsobjekten manipulieren.

Abbildung 3: Die Elemente des Vektors lassen sich auch durch die typische C++-Vorgehensweise mit Funktionsobjekten manipulieren.

Ist beliebige Funktionalität gewünscht, kommt aber auch die Leistungsfähigkeit der Funktionsobjekte an ihr Ende. Nun schlägt die Stunde der Lambda-Funktionen. Eine Lambda-Funktion, die direkt den Algorithmus »std::for_each« parametrisiert, kann beliebige Funktionalität umsetzen. In Listing 3 modifiziert in Zeile 13 die Lambda-Funktion »[](int& i){i=3*i+5;}« die Elemente des Vektors. Dabei leitet »[]« die Lambda-Funktion ein, »int& i« bezeichnet ihr Argument und »{i=3*i+5;}« stellt ihren Funktionskörper dar.

Listing 3

Lambda-Funktionen

01 #include <algorithm>
02 #include <cmath>
03 #include <iomanip>
04 #include <iostream>
05 #include <vector>
06
07
08 int main(){
09   std::cout << std::endl;
10
11   std::vector<int> myVec4{1,2,3,4,5,6,7,8,9,10};
12   std::cout << std::setw(20) << std::left << "myVec4: i->3*i+5:   ";
13   std::for_each(myVec4.begin(),myVec4.end(),[](int& i){i=3*i+5;});
14   for (auto v: myVec4) std::cout << std::setw(6) << std::left << v;
15   std::cout << "\n";
16
17   std::vector<int> myVec5{1,2,3,4,5,6,7,8,9,10};
18   std::cout << std::setw(20) << std::left << "myVec5: i->i*i   ";
19   std::for_each(myVec5.begin(),myVec5.end(),[](int& i){i=i*i;});
20   for (auto v: myVec5) std::cout << std::setw(6) << std::left << v;
21   std::cout << "\n";
22
23   std::vector<double> myVec6{1,2,3,4,5,6,7,8,9,10};
24   std::for_each(myVec6.begin(),myVec6.end(),[](double& i){i=std::sqrt(i);});
25   std::cout << std::setw(20) << std::left << "myVec6: i->sqrt(i): ";
26   for (auto v: myVec6) std::cout << std::fixed << std::setprecision(2)
   << std::setw(6) << v ;
27
28   std::cout << "\n\n";
29 }

Ähnlich kompakt gestalten sich die Lambda-Funktionen »[](int& i){i=i*i;}« in Zeile 19, die jedes Element des Vektors auf sein Quadrat, sowie die »[](double& i){i=sqrt(i);}« in Zeile 24, die jedes Element des Vektors auf seine Wurzel abbildet. Abbildung 4 zeigt die Ausgabe des Programmcode.

Abbildung 4: Mit Lambda-Funktionen kann der Programmierer den For-Each-Algorithmus parametrisieren.

Abbildung 4: Mit Lambda-Funktionen kann der Programmierer den For-Each-Algorithmus parametrisieren.

Nach dieser Einführung bietet der nächste Artikel etwas anspruchsvollere Lektüre, denn zu diesem Feature gibt es noch einige Fragen zu beantworten: Wie funktioniert eine Lambda-Funktion? Wie bindet sie ihren aufrufenden Bereich? Wann muss der Programmierer den Rückgabetyp einer Lambda-Funktion angeben? Wann soll ein Software-Entwickler Lambda-Funktionen einsetzen?

Es lohnt sich, die Lambda-Funktionen noch genauer zu studieren. Wer sie gut versteht, kann sie in Threads einsetzen oder auch in Form einer so genannten Closure [7]. Die kommende Folge dieser Serie wird sie daher an weiteren Beispielen vorführen. (mhu)

Feedback erwünscht

Fehlt Ihnen ein wichtiges C++11-Feature? Hat der Artikel einen wichtigen Aspekt übergangen? Unter mailto:cpp@linux-magazin.de erreichen Sie den Autor Rainer Grimm sowie die Redaktion. Ihr Feedback ist herzlich willkommen!

Infos

  1. Stroustrups FAQ zu C++0x: http://www2.research.att.com/~bs/C++0xFAQ.html#WG21
  2. Rainer Grimm, "Erfrischend Neu": Linux-Magazin 04/10, S. 116
  3. Rainer Grimm, "Reichhaltiges Angebot": Linux-Magazin 05/10, S. 110
  4. Pete Becker, "The C++ Standard Library Extension": Addison-Wesley 07/2006
  5. Anthony Williams, "C++ Concurrency in Action": Manning Publication 02/2010
  6. Marius Bancilia, "C++ Renaissance at Microsoft": http://mariusbancila.ro/blog/2011/06/20/cpp-renaissance-at-microsoft/
  7. Closure: http://en.wikipedia.org/wiki/Closure_%28computer_science%29

Der Autor

Rainer Grimm arbeitet seit 1999 als Software-Entwickler bei der Science + Computing AG in Tübingen. Insbesondere hält er Schulungen für das Produkt SC Venus. Im Dezember 2011 erscheint sein Buch "C++11".

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 3 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Als digitales Abo

Als PDF im Abo bestellen

comments powered by Disqus

Ausgabe 07/2013

Preis € 6,40

Insecurity Bulletin

Insecurity Bulletin

Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...

Linux-Magazin auf Facebook