Konstruktoren in C++ [] (Konstruktoren in C++), Lektion, Seite 723108
https://www.purl.org/stefan_ram/pub/konstruktoren_c++ (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram

Konstruktoren in C++ 

Objekterzeugung

Wir haben schon gesehen, daß zur Erzeugung eines Objekts die Definition einer Variablen des Typs des Objekts verwendet werden kann. Bei der Ausführung der Definition wird dann ein Objekt des Typs der Variablen erzeugt, und der Name der Variablen steht dann für dieses Objekt.

main.cpp
#include <iostream> // ::std::cout
#include <ostream> // << int main()
{ int v; /* <-- Erzeugung eines int-Objekts */
v = 2; /* <-- Schreiboperation */
::std::cout << v; /* <-- Leseoperation */
::std::cout << '\n'; }
::std::cout
2

Wir haben schon gesehen, daß zur Erzeugung eines Objekts die Definition einer Variablen des Typs des Objekts verwendet werden kann. Bei der Ausführung der Definition wird dann ein Objekt des Typs der Variablen erzeugt, und der Name der Variablen steht dann für dieses Objekt.

Exemplarerzeugung

Bestimmte Typen von C++  werden Klassen  genannt. Beispielsweise ist der Typ »::std::string« eine Klasse  während der Typ »int« keine  Klasse ist. Ob ein Typ eine Klasse ist, kann man der Spezifikation von C++  oder der Literatur zu C++  entnehmen.

Der Typ eines Objekts kann auch eine Klasse sein. Objekte, deren Typ eine Klasse ist, nennt man auch Objekte der Klasse  oder Exemplare der Klasse.

Ein Objekt einer Klasse wird in diesem Kurs manchmal „Exemplar“ genannt, um es von Objekten anderer Typen zu unterscheiden.

Das folgende Programm zeigt die Erzeugung eines Exemplars der Klasse »::std::string«. Dies wurde so schon im Grundkurs behandelt.

main.cpp
#include <iostream> // ::std::cout
#include <ostream> // <<
#include <string> // ::std::string using namespace ::std::literals; int main()
{ ::std::string v; /* <-- Erzeugung eines ::std::string-Objekts */
v = "2"s; /* <-- Schreiboperation */
::std::cout << v; /* <-- Leseoperation */
::std::cout << '\n'; }
::std::cout
2

Um ein Exemplar einer Klasse anzulegen, kann eine Variable definiert werden, welche jene Klasse als Typ hat.

Bei der Ausführung der Deklaration und vor der Verwendung des Exemplars in der Ausgabeanweisung wird ein Konstruktor aus der Klasse »::std::string« ausgeführt. Dieser Konstruktor ist in dem obigen Programm nicht direkt sichtbar (nicht abgebildet). Er sorgt dafür, daß ein ::std::string-Exemplar »v« auf korrekte Weise so angelegt wird, daß sein innerer Zustand den Anforderungen für ein Objekt jener Klasse genügt. Deswegen kann es dann im Rest des Programms ohne Probleme verwendet werden.

Die Begriffe in der C++ -Spezifikation *

In der C++-Spezifikation findet man 2015 sowohl “object of a class ” als auch “instance of a class ”, ohne daß die Bedeutung dieser beiden Begriffe sich anscheinend unterscheiden soll. Das alleinstehende Wort “instance ” findet sich darin aber auch in vielen anderen Bedeutungen wie “instance of the abstract machine ”, “instance of a backslash character ”, “instance of a function template. ”, “instance of an incompletely-defined object type ”, “instances of the name ”, “instance of a ## preprocessing token ”, “instance of the function class template ”, “instances of class templates ”, “instance of the template type argument ” oder “instance of their traits template parameter ”.

C++ 2015 9p1 *
An object of a class consists of a (possibly empty) sequence of members and base class objects.
C++ 2015 22.4.8p10 *
A locale object may be extended with a new facet simply by constructing it with an instance of a class derived from locale::facet.
C++ 2015 22.5.5.1p1 *
an instance of unordered_multimap may contain multiple copies of each key value
C++ 2015 20.8.2.2.2p2 (“instances ” ohne “of ”) *
Since the destruction of *this decreases the number of instances that share ownership with *this by one
C++ 2015 20.8.2.6p1 (“instances ” ohne “of ” als Bezug auf “object ” einer Klasse) *
Concurrent access to a shared_ptr object from multiple threads does not introduce a data race if the access is done exclusively via the functions in this section and the instance is passed as their first argument.
C++ 2015 26.5.8.6.1p7 (“instances ” mit “of ” als Bezug auf “object ” einer Klasse) *
Each instance of type UnaryOperation shall be a function object
C++ 2015 27.5.3.1.6p4 (“instances ” mit “of ” als Bezug auf “object ” einer Klasse) *
Destroys an object of class Init. If there are no other instances of the class still in existence, calls cout.flush()

Konstruktoren

Die erste Festlegung des Zustandes eines neu erzeugten Exemplars einer Klasse nennt man Initialisierung.

Ein Konstruktor  ist eine Funktion einer Klasse, die ein neues Exemplar einer Klasse initialisieren kann.

The default constructor (12.1), copy constructor and copy assignment operator (12.8), move constructor and move assignment operator (12.8), and destructor (12.4) are special member functions. C++ 2015 12p1s1

main.cpp
#include <iostream> // ::std::cout
#include <ostream> // <<
#include <string> // ::std::string using namespace ::std::literals; int main()
{ ::std::string const v( "alpha"s );
::std::cout << v << '\n'; }
::std::cout
alpha

In dem voranstehenden Programm wird ein ::std::string-Exemplar »v« durch eine Direkt-Initialisierung mit der Ausdruckliste »"alpha"s« angelegt. (Wir sprechen hier zur Unterscheidung von anderen Ausdrücken des Programms von einer „Ausdruckliste“, auch wenn diese Ausdruckliste hier nur genau einen Ausdruck enthält.)

Bei der Ausführung der Deklaration und vor der Verwendung des Exemplars in der Ausgabeanweisung wird ein Konstruktor aus der Klasse »::std::string« ausgeführt. Dieser Konstruktor ist in dem obigen Programm nicht direkt sichtbar (nicht abgebildet). Er sorgt dafür, daß das ::std::string-Exemplar »v« den Text der Ausdruckliste »"alpha"s« erhält. Entsprechend bewirkt die Ausgabe von »v« dann die Ausgabe des Textes »alpha«.

Anderer Typ der Ausdruckliste

In dem folgenden Programm wird ein ::std::string-Exemplar »v« durch eine Direkt-Initialisierung mit der Ausdruckliste »"alpha"« angelegt.

main.cpp
#include <iostream> // ::std::cout
#include <ostream> // <<
#include <string> // ::std::string int main()
{ ::std::string const v( "alpha" );
::std::cout << v << '\n'; }
::std::cout
alpha

Bei der Ausführung der Deklaration und vor der Verwendung des Exemplars in der Ausgabeanweisung wird ein Konstruktor aus der Klasse »::std::string« ausgeführt. Dieser Konstruktor ist in dem obigen Programm nicht direkt sichtbar (nicht abgebildet). Er sorgt dafür, daß das ::std::string-Exemplar »v« den Text der Ausdruckliste »"alpha"« erhält. Entsprechend bewirkt die Ausgabe von »v« dann die Ausgabe des Textes »alpha«.

Weiterer Typ der Ausdruckliste

In dem folgenden Programm wird ein ::std::string-Exemplar »v« durch eine Direkt-Initialisierung mit der Ausdruckliste »3, 'c'« angelegt.

main.cpp
#include <iostream> // ::std::cout
#include <ostream> // <<
#include <string> // ::std::string int main()
{ ::std::string const v( 3, 'c' );
::std::cout << v << '\n'; }
::std::cout
ccc

Bei der Ausführung der Deklaration und vor der Verwendung des Exemplars in der Ausgabeanweisung wird ein Konstruktor aus der Klasse »::std::string« ausgeführt. Dieser Konstruktor ist in dem obigen Programm nicht direkt sichtbar (nicht abgebildet). Er sorgt dafür, daß das ::std::string-Exemplar »v« als Text eine Folge von drei »c« erhält. Entsprechend bewirkt die Ausgabe von »v« dann die Ausgabe des Textes »ccc«.

Referenzparameter

Ein Parameter kann an Stelle des Typs »::std::string« auch den Typ »::std::string const &« oder den Typ »::std::string & &« haben.

Für einen solchen Parameter können dieselben Argumente verwendet werden, wie bei einem Parameter vom Typ »::std::string«.

Der Unterschied besteht nur darin, daß im Falle von Parameter mit einem »&« bei Objekten als Argument der Zustand des Objekts bei der Übergabe nicht kopiert wird. Vielmehr wird dann das Objekt selber übergeben.

Dies kann nötig sein, wenn das Kopieren vermieden werden soll, weil das Objekt entweder nicht kopiert werden kann oder das Kopieren zu lange dauern würde.

Falls ein Wert als Argument angegeben wird, der kein Objekt ist, so wird dabei automatisch ein Objekt mit dem angegebenen Wert erzeugt und übergeben.

In der Klasse »::std::string« hat der Konstruktor »::std::string( ::std::string const & s );« einen Parameter als konstante Referenz, um zu verhindern, daß das übergebene Argumentobjekt bei der Übergabe kopiert wird. Im Zuge solch einer Kopie könnte nämlich derselbe Konstruktor erneut verwendet werden, da dieser zum Kopieren von Objekt herangezogen wird. So könnte sich eine unerwünschte unendliche Rekursion ergeben, die zu einem Programmabbruch oder einer Programmblockade führen könnte.

Auswahl von Konstruktoren

Wir haben gesehen, daß ein ::std::string-Exemplar mit Ausdruckslisten verschiedener Typen angelegt werden kann.

In der Dokumentation der Klasse »::std::string« findet verschiedene Konstruktoren.

Dokumentation der Klasse »::std::string« nach C++ 2015, 21.4.2 (vereinfacht und übersetzt)
 
::std::string();
Anlegen eines leeren ::std::string-Exemplars.
 
::std::string( ::std::string const & s );
Anlegen eines ::std::string-Exemplars mit dem Text eines anderen ::std::string-Exemplars »s«.
 
::std::string( char const * s );
Anlegen eines ::std::string-Exemplars mit dem Text einer Zeichenfolge »s«. (Hier darf der Wert von »s« kein Nullzeiger sein.)
 
::std::string( int n, char c );
Anlegen eines ::std::string-Exemplars mit dem Text einer n-fachen Wiederholung des Zeichens »c«.
 

Bei der Direktinitialisierung wird der passende Konstruktor wie bei der Überladung einer Funktion an Hand des Typs der Ausdruckliste ausgewählt (nach C++ 2015, 13.3.1.3 Initialization by constructor [over.match.ctor]).

Entsprechend wird jeder der oben aufgelisteten Konstruktoren einem Programmbeispiel zugeordnet, und die Bedeutung der Ausdruckliste kann dann jeweils der Dokumentation des Konstruktors entnommen werden.

Ein Konstruktor kann zwar nicht direkt aufgerufen werden, aber eine Direktinitialisierung kommt einem „Konstruktoraufruf“ nahe.

Im folgenden zeigen wir die oben verwendeten Variablendefinition und die jeweils zugehörigen Konstruktoren.

Definition und verwendeter Konstruktor
Definition: »::std::string v;«
verwendeter Konstruktor: »::std::string();«
Definition und verwendeter Konstruktor
Definition: »::std::string const v( "alpha"s );«
verwendeter Konstruktor: »::std::string( ::std::string & s );«
Definition und verwendeter Konstruktor
Definition: »::std::string const v( "alpha" );«
verwendeter Konstruktor: »::std::string( char const * s );«
Definition und verwendeter Konstruktor
Definition: »::std::string const v( 3, 'c' );«
verwendeter Konstruktor: »::std::string( int n, char c );«

Kopierkonstruktoren

Ein Kopierkonstruktor einer Klasse ist ein Konstruktor in der Dokumentation dieser Klasse, der genau ein Argument vom Typ dieser Klasse akzeptiert. Hier wird aber als Parameter eine konstante Referenz auf den Typ dieser Klasse verwendet, wie weiter oben begründet wurde.

Wir können damit sagen, daß der Konstruktor, der in der obenstehende Liste mit Dokumentation als »::std::string( ::std::string & s );« dokumentiert ist, ein Kopierkonstruktor der Klasse »::std::string« ist, während die andere gezeigten Konstruktoren keine  Kopierkonstruktoren sind.

Übungsfragen

?   Kopierkonstruktoren

Welche der folgenden drei Beschreibungen von Konstruktoren der Klasse »::std::regex« beschreiben einen Kopierkonstruktor?

Dokumentation der Klasse »::std::regex« (vereinfacht)
::std::regex( ::std::string & s );
::std::regex( char const * s );
::std::regex( ::std::regex & r );

?   Auflösung von Deklarationen

Ordnen Sie jeder Deklaration aus dem Rumpf der Funktion »main« des folgenden Programmes einen Konstruktor der Klasse »::std::regex« aus der Auflistung in der vorigen Übungsfrage zu!

main.cpp
#include <iostream>
#include <ostream>
#include <string>
#include <regex>

using namespace ::std::literals;

int main()
{ ::std::regex a( "abc"s );
::std::regex b( a ); }

?   Erzeugung von int-Objekten

main.cpp
#include <iostream> // ::std::cout
#include <ostream> // <<
#include <cstdlib> // ::std::rand
#include <string> // ::std::string

using namespace ::std::literals;

void f( int const depth )
{ if( depth < 3 )
{ int v = ::std::rand();
::std::cout << "depth " << depth << ": v = "s << v << '\n';
if( depth == 2 )::std::cout << "---\n"s;
f( depth + 1 );
::std::cout << "depth " << depth << ": v = "s << v << '\n'; }}

int main() { f( 0 ); }
::std::cout
depth 0: v = 41
depth 1: v = 18467
depth 2: v = 6334
---
depth 2: v = 6334
depth 1: v = 18467
depth 0: v = 41

Wie viele int-Objekte existieren zum Zeitpunkt der Ausgabe von »---« in dem obenstehenden Programm?

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 stefanram723108 stefan_ram:723108 Konstruktoren in C++ Stefan Ram, Berlin, and, or, near, uni, online, slrprd, slrprdqxx, slrprddoc, slrprd723108, slrprddef723108, 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/konstruktoren_c++