Initialisierungslistenkonstruktoren in C++ [] (Initialisierungslistenkonstruktoren in C++), Lektion, Seite 723684
https://www.purl.org/stefan_ram/pub/initialisierungslistenkonstruktoren_c++ (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram
C++-Kurs

Initialisierungslistenkonstruktoren in C++

Der Konstruktor für Zeichenwiederholungen

Bei der folgenden Direktinitialisierung wird ein Konstruktor herangezogen, der eine Zeichenfolge mit der angegebenen Anzahl (»3«) von Wiederholungen eines Zeichens (»'B'«) initialisiert.

main.cpp

#include <initializer_list>
#include <ostream>
#include <iostream>

int main()
{ ::std::string s( 3, 'B' );
::std::cout << s << '\n'; }

transcript
BBB

Man sieht, daß sich eine Zeichenfolge aus drei »B« ergibt.

Die Klasse »::std::string« enthält einen parametrisierten Konstruktor »string( int n, char c )«, mit dem eine Zeichenfolge durch eine Zahl »n« und ein Zeichen »c« initialisiert werden kann, so daß sie das Zeichen »c« so oft enthält, wie die Zahl »n« angibt.

Proklamation der Klasse »::std::string« (vereinfacht)

namespace std
{ using string = basic_string< char >;

template< class charT >
class basic_string
{ basic_string( basic_string const str );
basic_string( charT const * );
basic_string( int n, charT c );
charT const * c_str() const;
charT const * data() const; }}

Es ist jener Konstruktor, welcher zur Initialisierung des Objektes »s« bei der Ausführung der Deklarationsanweisung »::std::string s( 3, 'B' );« herangezogen wird. Dies führt dazu, daß die Zeichenfolge »s« als eine Folge von drei »B« angelegt wird.

Kennzahlen für Schriftzeichen

Ein Zeichen kann auch durch seine Kennzahl  angegeben werden. Die Kennzahl eines Zeichens ist aber implementationsspezifisch. Unter vielen Implementationen hat das Zeichen »A« beispielsweise die Kennzahl »65«. Wir nehmen im folgenden eine C++ -Implementation mit der untenstehenden Zeichentabelle an.

Zeichentabelle

Zeichen Kennzahl

A 65
B 66

Wenn im Initialisierer zwei int-Ausdrücke stehen, wird weiterhin  der parametrisierten Konstruktor »string( int n, char c )« herangezogen (und der Wert des zweiten int-Ausdrucks nach »char« gewandelt).

main.cpp

#include <initializer_list>
#include <ostream>
#include <iostream>
#include <string>

int main()
{ { ::std::string s( 3, 66 ); ::std::cout << s << '\n'; }
{ ::std::string s( 3, 65 ); ::std::cout << s << '\n'; }}

transcript
BBB
AAA

In dem obenstehenden Programm werden drei Zeichen mit der Kennzahl »66« ausgegeben, also »BBB«. Es folgenden 3 »A« (Zeichen mit der Kennzahl 65).

Direkt-Listeninitialisierung

Wir können jetzt aber beobachten, daß wir bei Verwendung eines Initialisierers mit geschweiften  Klammern andere  Ergebnisse erhalten.

main.cpp

#include <initializer_list>
#include <ostream>
#include <iostream>
#include <string>

int main()
{ { ::std::string s( 65, 66 ); ::std::cout << s << '\n'; }
{ ::std::string s{ 65, 66 }; ::std::cout << s << '\n'; }
{ ::std::string s( 66, 65 ); ::std::cout << s << '\n'; }
{ ::std::string s{ 66, 65 }; ::std::cout << s << '\n'; }}

transcript

BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
AB
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BA

Bei Verwendung des Initialisierers »{ 65, 66 }« wird ein Konstruktor herangezogen, den wir bisher noch nicht vorgestellt  haben.

Es handelt sich um den Listenkonstruktor  »string( ::std::initializer_list< charT > )«.

Proklamation der Klasse »::std::string« (vereinfacht)

namespace std
{ using string = basic_string< char >;

template< class charT >
class basic_string
{ basic_string( basic_string const str );
basic_string( charT const * );
basic_string( int n, charT c );
basic_string(::std::initializer_list< charT > );
charT const * c_str() const;
charT const * data() const; }}

Dies ist ein Konstruktor, der für den Fall einer Initialisierung mit einer Initialisierungsliste, zu der auch die Direkt-Listeninitialisierung gehört, gedacht ist. Er wird immer dann herangezogen, wenn eine Initialisierungsliste zur Initialisierung verwendet wird, auch wenn es noch einen passenden anderen Konstruktor gegeben hätte.

Der Konstruktor »basic_string(::std::initializer_list< charT > )« initialisiert eine Zeichenfolge mit den Schriftzeichen, deren Kennzahlen in der Initialisierungsliste stehen. Somit führt »::std::string s{ 65, 66 };« zur Initialisierung einer Zeichenfolgen mit den beiden Zeichen, deren Kennzahlen »65« beziehungsweise »66« lauten, also ergibt sich die Zeichenfolge »"AB"«.

Wenn es einen passenden Listenkonstruktor gibt, so erhält dieser bei einer Listen-Initialisierung den Vorrang.

In C++  wird heute allgemein die Listeninitialisierung empfohlen. die als „universelle“ gilt. Sie kann jedoch nicht verwendet werden, wenn bestimmte Konstruktoren herangezogen werden sollen, die eine geringerere Priorität als ein Listenkonstruktor haben. Daher kann man nur sagen, daß man die Direkt-Listeninitialisierung verwenden sollte, falls keine andere Form der Initialisierung nötig ist, um einen gewünschten Konstruktor heranzuziehen. Im Falle der Klasse »::std::string« muß ein Initialisierer mit runden Klammern verwendet werden, wenn eine Initialisierung verwendet werden soll, die den Konstruktor »basic_string( int n, charT c )« heranzieht, der eine Initialisierung mit der Angabe einer Anzahl von Wiederholungen eines Zeichens erlaubt.

Umgekehrt können wir sagen, daß in Klassen, die keine Listenkonstruktoren enthalten, die Direktinitialisierung mit runden und geschweiften Klammern jeweils dieselben Konstruktoren heranzieht.

Zitat C++ 2016-11
13.3.1.7 Initialization by list-initialization [over.match.list]
1 When objects of non-aggregate class type T are list-initialized such that 8.6.4 specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor in two phases:
(1.1) — Initially, the candidate functions are the initializer-list constructors (8.6.4) of the class T and the argument list consists of the initializer list as a single argument.
(1.2) — If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.
If the initializer list has no elements and T has a default constructor, the first phase is omitted.
In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.

Initialisierungen von Vektoren

Auch bei Vektoren gibt es die Unterscheidung zwischen einem Listenkonstruktor und einem Nicht-Listen-Konstruktor.

main.cpp
#include <initializer_list>
#include <ostream>
#include <iostream>
#include <string>
#include <vector> int main()
{ { ::std::vector< int >v( 2, 3 ); ::std::cout << v.size() << v.at( 0 )<< v.at( 1 )<< '\n'; }
{ ::std::vector< int >v{ 2, 3 }; ::std::cout << v.size() << v.at( 0 )<< v.at( 1 )<< '\n'; }}
transcript
233
223

Während der Nicht-Listen-Konstruktor mit zwei Parametern einen Vektor mit einer durch das erste Argument angegebenen Anzahl von Wiederholung des zweiten Argumentes initialisiert, initialisiert der Listenkonstruktor mit den in der Liste angegebenen Werten.

Die leere Initialisierungsliste

Es gibt eine Sonderregel für die leere Initialisierungsliste »{}«: Sie führt bei einer Klasse als Typ immer zur Verwendung des Fehlkonstruktors. Ein etwaiger Initialisierungslistenkonstruktor  wird hier nie  herangezogen.

main.cpp

#include <initializer_list>
#include <ostream>
#include <iostream>
#include <string>

int main()
{ ::std::string s{}; ::std::cout << s << '\n'; }

Protokoll

Dementsprechend hat die Initialisierung von »s« in dem folgenden Programm dieselbe Bedeutung wie in dem direkt über diesem Satz stehendem Programm.

main.cpp

#include <ostream>
#include <iostream>
#include <string>

int main()
{ ::std::string s; ::std::cout << s << '\n'; }

Protokoll

Übungsfragen

?   Initialisierungen

Es sei „T  “ ein Typ, „x “ ein Name, „A “ eine kommagetrennte Ausdruckliste, und „a “ ein Ausdruck.

T   x ( A  ); wird Direktinitialisierung  genannt

T   x { A  };  wird Direktinitialisierung  und auch Direkt-Listeninitialisierung  genannt.

T   x  = a ; wird Kopierinitialisierung  genannt.

Bestimmen Sie nun um welche Art von Initialisierung es sich bei den folgenden Initialisierungen handelt!

Anton
::std::string s( 65, 66 );
Berta
::std::string s{ 65, 66 };
Cäsar
auto first { 8 };
Dora
::std::string const b = a;
Emil
auto const compound = ::std::make_pair( 2, 7 );
Friedrich
int const a( 7 );
Gustav
double const x = 2;
Heinrich
auto const compound { ::std::make_pair( 2, 7 ) };
Ida
::std::string const b =( a );

?   Konstruktoren

Falls eine Listeninitialisierung verwendet wird, so werden Listenkonstruktoren bevorzugt herangezogen. Bei Klassen ohne Listenkonstruktor wird für eine Direkt-Listeninitialisierung in der Regel hingegen derselbe Konstruktor herangezogen wie bei einer entsprechenden Listeninitialisierung mit runden Klammern.

Sagen Sie nun voraus, welcher Konstruktor der Beispielklassen für eine gegebene Initialisierung herangezogen wird.

Proklamation der Klasse »::std::vector« (vereinfacht)
namespace std
{ template< class T > class vector
{ using size_type = unsigned int;
vector( size_type n, T value ); /* Anton */
vector( initializer_list< T > list ); /* Berta */ }}
main.cpp

#include <initializer_list>
#include <ostream>
#include <iostream>
#include <vector>

int main()
{ {
::std::vector< int > v( 65, 66 ); /* Caesar */
::std::cout << v.at( 0 )<< '\n'; }
{
::std::vector< int > v{ 65, 66 }; /* Dora */
::std::cout << v.at( 0 )<< '\n'; }}

Dokumentation (vereinfacht)
// defined in header <utility>
namespace std
{ template< class T1, class T2 >
struct pair
{ T1 first;
T2 second;
pair( pair ); /* Emil */
pair( const T1 x, const T2 y ); /* Friedrich */
}; }
main.cpp

#include <initializer_list>
#include <ostream>
#include <iostream>
#include <vector>

int main()
{ {
::std::pair< int, int > p{ 1, 2 }; /* Gustav */
::std::cout << p.first << '\n'; }
{
::std::pair< int, int > p( 1, 2 ); /* Heinrich */
::std::cout << p.first << '\n'; }}

Übungsaufgaben

/   Unterstreichen

Schreiben Sie eine Definition einer Funktion »u«, welche ihre Argumentzeichenfolge so unterstreicht, wie unten gezeichnet.

Argumentzeichenfolge
abc
Ergebnistext
abc
---

Die Länge der Unterstreichung soll sich der Länge der Argumentzeichenfolge (0 – 40 Zeichen) anpassen.

Argumentzeichenfolge
Zypressenlabyrinth
Ergebnistext
Zypressenlabyrinth
------------------

/   Wertfolgen

Verwenden Sie die Typschablone »::std::vector« mit dem Typargument »double«, um ein Objekt anzulegen, das eine Folge der drei double-Werte »1.2«, »2.2« und »3.6« enthält. Jene drei Werte sollen schon bei der Initialisierung festgelegt werden und nicht erst später mit einer Funktion wie »push_back« hinzugefügt werden. Geben Sie dann die ersten drei Komponente jenes Objektes aus, indem Sie die Elementfunktion »at(int)« (vereinfachter Parametertyp) mit den Werten »0«, »1« und »2« aufrufen.

Ausgabe des Programms
1.2
2.2
3.6

/   Einrahmen ⃗

Schreiben Sie eine Definition einer Funktion »u«, welche ihre Argumentzeichenfolge so einrahmt, wie unten gezeichnet.

Argumentzeichenfolge
abc
Ergebnistext
*******
* abc *
*******

Der Rahmen soll sich der Länge der Argumentzeichenfolge (0 – 40 Zeichen) anpassen.

Argumentzeichenfolge
Zypressenlabyrinth
Ergebnistext
**********************
* Zypressenlabyrinth *
**********************
8.6.4 List-initialization [dcl.init.list]

Seiteninformationen und Impressum   |   Mitteilungsformular  |   "ram@zedat.fu-berlin.de" (ohne die Anführungszeichen) ist die Netzpostadresse von Stefan Ram.   |   Eine Verbindung zur Stefan-Ram-Startseite befindet sich oben auf dieser Seite hinter dem Text "Stefan Ram".)  |   Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram. Schlüsselwörter zu dieser Seite/relevant keywords describing this page: Stefan Ram Berlin slrprd slrprd stefanramberlin spellched stefanram723684 stefan_ram:723684 Initialisierungslistenkonstruktoren in C++ Stefan Ram, Berlin, and, or, near, uni, online, slrprd, slrprdqxx, slrprddoc, slrprd723684, slrprddef723684, PbclevtugFgrsnaEnz Erklärung, Beschreibung, Info, Information, Hinweis,

Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram.
https://www.purl.org/stefan_ram/pub/initialisierungslistenkonstruktoren_c++