Namensräume
Varianten
Aktionen

Object

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

C++ Programme erstellen, zerstören, referenzieren, zugreifen auf und manipulieren Objekte.

Ein Objekt hat in C++

Die folgenden Entitäten sind keine Objekte: Wert, Referenz, Funktion, Aufzählungsmitglied, Typ, nicht-statisches Klassenmitglied, Template, Klassen- oder Funktionstemplate-Spezialisierung, Namensraum, Parameterpaket und this.

Ein Variable ist ein Objekt oder eine Referenz, die kein nicht-statisches Datenelement ist und durch eine Deklaration eingeführt wird.

Inhalt

[bearbeiten] Objekterzeugung

Objekte können explizit durch Definitionen, new-Ausdrücken, throw-Ausdrücken, dem Ändern des aktiven Mitglieds einer union und der Auswertung von Ausdrücken, die temporäre Objekte erfordern, erstellt werden. Das erstellte Objekt ist bei expliziter Objekterzeugung eindeutig definiert.

Objekte von Implicit-Lifetime-Typen können auch implizit erstellt werden durch

  • außerhalb der konstanten Auswertung, Operationen, die die Lebensdauer eines Arrays vom Typ unsigned char oder std::byte(seit C++17) beginnen, in diesem Fall werden solche Objekte im Array erstellt,
  • Aufrufe folgender alloziierender Funktionen, in diesem Fall werden solche Objekte im allozierten Speicher erstellt
(seit C++17)
  • Aufrufe folgender Objektdarstellungs-Kopierfunktionen, in diesem Fall werden solche Objekte in der Zielregion des Speichers oder im Ergebnis erstellt
(seit C++20)
  • Aufrufe folgender spezifischer Funktionen, in diesem Fall werden solche Objekte in der angegebenen Speicherregion erstellt
  • std::start_lifetime_as
  • std::start_lifetime_as_array
(seit C++23)

Null oder mehr Objekte können im selben Speicherbereich erstellt werden, solange dies dem Programm ein definiertes Verhalten verleiht. Wenn eine solche Erzeugung unmöglich ist, z. B. aufgrund widersprüchlicher Operationen, ist das Verhalten des Programms undefiniert. Wenn mehrere solcher Sätze implizit erzeugter Objekte dem Programm ein definiertes Verhalten verleihen würden, ist nicht spezifiziert, welcher solche Satz von Objekten erzeugt wird. Mit anderen Worten, implizit erzeugte Objekte müssen nicht eindeutig definiert sein.

Nachdem Objekte innerhalb eines angegebenen Speicherbereichs implizit erstellt wurden, erzeugen einige Operationen einen Zeiger auf ein geeignetes erzeugtes Objekt. Das geeignete erzeugte Objekt hat dieselbe Adresse wie der Speicherbereich. Ebenso ist das Verhalten undefiniert, wenn kein solcher Zeigerwert dem Programm ein definiertes Verhalten verleiht, und es ist nicht spezifiziert, welcher Zeigerwert erzeugt wird, wenn mehrere Werte ein definiertes Verhalten verleihen.

#include <cstdlib>
 
struct X { int a, b; };
 
X* MakeX()
{
    // One of possible defined behaviors:
    // the call to std::malloc implicitly creates an object of type X
    // and its subobjects a and b, and returns a pointer to that X object
    X* p = static_cast<X*>(std::malloc(sizeof(X)));
    p->a = 1;
    p->b = 2;
    return p;
}

Aufrufe von std::allocator::allocate oder implizit definierte Kopier-/Verschiebe-Spezialmemberfunktionen von union-Typen können ebenfalls Objekte erstellen.

[bearbeiten] Objektdarstellung und Wertdarstellung

Einige Typen und Objekte haben Objektdarstellungen und Wertdarstellungen, die in der folgenden Tabelle definiert sind

Entität Objektdarstellung Wertdarstellung
ein vollständiger Objekttyp T die Sequenz von N unsigned char Objekten, die von einem vollständigen Objekt vom Typ T ohne Bitfeld belegt werden, wobei N gleich sizeof(T) ist die Menge von Bits in der Objektdarstellung von T, die an der Darstellung eines Werts vom Typ T beteiligt sind
ein vollständiges Objekt obj ohne Bitfeld vom Typ T die Bytes von obj, die der Objektdarstellung von T entsprechen die Bits von obj, die der Wertdarstellung von T entsprechen
ein Bitfeldobjekt bf die Sequenz von N Bits, die von bf belegt werden, wobei N die Breite des Bitfelds ist die Menge von Bits in der Objektdarstellung von bf, die an der Darstellung des Werts von bf beteiligt sind

Bits in der Objektdarstellung eines Typs oder Objekts, die nicht Teil der Wertdarstellung sind, sind Füllbits.

Für TriviallyCopyable-Typen ist die Wertdarstellung Teil der Objektdarstellung, was bedeutet, dass das Kopieren der vom Objekt im Speicher belegten Bytes ausreicht, um ein anderes Objekt mit demselben Wert zu erzeugen (außer wenn das Objekt ein potenziell überlappendes Unterobjekt ist oder der Wert eine Trap-Darstellung seines Typs ist und das Laden in die CPU eine Hardware-Ausnahme auslöst, wie z. B. SNaN ("signalling not-a-number") Gleitkommawerte oder NaT ("not-a-thing") Ganzzahlwerte).

Obwohl die meisten Implementierungen keine Trap-Darstellungen, Füllbits oder mehrere Darstellungen für Ganzzahltypen zulassen, gibt es Ausnahmen; z.B. kann ein Wert eines Ganzzahltyps auf Itanium eine Trap-Darstellung sein.

Das Umgekehrte ist nicht unbedingt der Fall: Zwei Objekte eines TriviallyCopyable-Typs mit unterschiedlichen Objektdarstellungen können denselben Wert darstellen. Zum Beispiel repräsentieren mehrere Gleitkomma-Bitmuster denselben Spezialwert NaN. Häufiger können Füllbits eingefügt werden, um Ausrichtungsanforderungen, Bitfeld-Größen usw. zu erfüllen.

#include <cassert>
 
struct S
{
    char c;  // 1 byte value
             // 3 bytes of padding bits (assuming alignof(float) == 4)
    float f; // 4 bytes value (assuming sizeof(float) == 4)
 
    bool operator==(const S& arg) const // value-based equality
    {
        return c == arg.c && f == arg.f;
    }
};
 
void f()
{
    assert(sizeof(S) == 8);
    S s1 = {'a', 3.14};
    S s2 = s1;
    reinterpret_cast<unsigned char*>(&s1)[2] = 'b'; // modify some padding bits
    assert(s1 == s2); // value did not change
}

Für Objekte vom Typ char, signed char und unsigned char (sofern es sich nicht um übergroße Bitfelder handelt) müssen alle Bits der Objektdarstellung an der Wertdarstellung beteiligt sein und jedes mögliche Bitmuster repräsentiert einen einzelnen Wert (keine Füllbits, Trap-Bits oder Mehrfachdarstellungen zulässig).

[bearbeiten] Unterobjekte

Ein Objekt kann Unterobjekte haben. Dazu gehören

  • Mitgliedsobjekte
  • Basisklassen-Unterobjekte
  • Array-Elemente

Ein Objekt, das kein Unterobjekt eines anderen Objekts ist, wird als vollständiges Objekt bezeichnet.

Wenn ein vollständiges Objekt, ein Mitgliedsunterobjekt oder ein Array-Element von Klassentyp ist, gilt sein Typ als die am meisten abgeleitete Klasse, um ihn vom Klassentyp eines Basisklassen-Unterobjekts zu unterscheiden. Ein Objekt eines am meisten abgeleiteten Klassentyps oder eines Nicht-Klassentyps wird als am meisten abgeleitetes Objekt bezeichnet.

Für eine Klasse,

werden als ihre potenziell zu konstruierenden Unterobjekte bezeichnet.

[bearbeiten] Größe

Ein Unterobjekt ist ein potenziell überlappendes Unterobjekt, wenn es ein Basisklassen-Unterobjekt ist oder ein nicht-statisches Datenelement, das mit dem Attribut [[no_unique_address]] deklariert ist(seit C++20).

Ein Objekt obj kann nur eine Größe von Null haben, wenn alle folgenden Bedingungen erfüllt sind

  • obj ist ein potenziell überlappendes Unterobjekt.
  • obj ist vom Typ einer Klasse ohne virtuelle Memberfunktionen und virtuelle Basisklassen.
  • obj hat keine Unterobjekte mit Nicht-Null-Größe oder unbenannte Bitfelder mit Nicht-Null-Länge.

Für ein Objekt obj, das alle oben genannten Bedingungen erfüllt

  • Wenn obj ein Basisklassen-Unterobjekt eines standard-layout(seit C++11) Klassentyps ohne nicht-statische Datenelemente ist, hat es die Größe Null.
  • Andernfalls ist es implementierungsabhängig, unter welchen Umständen obj die Größe Null hat.

Siehe Optimierung für leere Basisklassen für weitere Details.

Jedes Nicht-Bitfeld-Objekt mit einer Größe ungleich Null muss einen oder mehrere Bytes Speicherplatz belegen, einschließlich jedes Bytes, das (ganz oder teilweise) von einem seiner Unterobjekte belegt wird. Der belegte Speicher muss zusammenhängend sein, wenn das Objekt von einem trivially copyable oder standard-layout(seit C++11) Typ ist.

[bearbeiten] Adresse

Sofern ein Objekt kein Bitfeld oder ein Unterobjekt der Größe Null ist, ist die Adresse dieses Objekts die Adresse des ersten Bytes, das es belegt.

Ein Objekt kann andere Objekte enthalten, in diesem Fall sind die enthaltenen Objekte in das erstere Objekt verschachtelt. Ein Objekt a ist in einem anderen Objekt b verschachtelt, wenn eine der folgenden Bedingungen erfüllt ist

  • a ist ein Unterobjekt von b.
  • b stellt Speicher für a bereit.
  • Es existiert ein Objekt c, wobei a in c verschachtelt ist und c in b verschachtelt ist.

Ein Objekt ist ein potenziell nicht eindeutiges Objekt, wenn es eines der folgenden Objekte ist

(seit C++11)
  • Ein Unterobjekt eines potenziell nicht eindeutigen Objekts.

Für zwei Nicht-Bitfeld-Objekte mit überlappenden Lebensdauern

  • Wenn eine der folgenden Bedingungen erfüllt ist, können sie dieselbe Adresse haben
  • Eines von ihnen ist in das andere verschachtelt.
  • Eines von ihnen ist ein Unterobjekt der Größe Null und ihre Typen sind nicht ähnlich.
  • Sie sind beide potenziell nicht eindeutige Objekte.
  • Andernfalls haben sie immer unterschiedliche Adressen und belegen getrennte Bytes im Speicher.
// character literals are always unique
static const char test1 = 'x';
static const char test2 = 'x';
const bool b = &test1 != &test2;      // always true
 
// the character 'x' accessed from “r”, “s” and “il”
// may have the same address (i.e., these objects may share storage)
static const char (&r) [] = "x";
static const char *s = "x";
static std::initializer_list<char> il = {'x'};
const bool b2 = r != il.begin();      // unspecified result
const bool b3 = r != s;               // unspecified result
const bool b4 = il.begin() != &test1; // always true
const bool b5 = r != &test1;          // always true

[bearbeiten] Polymorphe Objekte

Objekte eines Klassentyps, die mindestens eine virtuelle Funktion deklarieren oder erben, sind polymorphe Objekte. Innerhalb jedes polymorphen Objekts speichert die Implementierung zusätzliche Informationen (bei jeder existierenden Implementierung ist dies ein Zeiger, es sei denn, er wird optimiert), die für virtuelle Funktionsaufrufe und für die RTTI-Funktionen (dynamic_cast und typeid) verwendet werden, um zur Laufzeit den Typ zu bestimmen, mit dem das Objekt erstellt wurde, unabhängig vom Ausdruck, in dem es verwendet wird.

Für nicht-polymorphe Objekte wird die Interpretation des Werts aus dem Ausdruck bestimmt, in dem das Objekt verwendet wird, und zur Kompilierzeit festgelegt.

#include <iostream>
#include <typeinfo>
 
struct Base1
{
    // polymorphic type: declares a virtual member
    virtual ~Base1() {}
};
 
struct Derived1 : Base1
{
     // polymorphic type: inherits a virtual member
};
 
struct Base2
{
     // non-polymorphic type
};
 
struct Derived2 : Base2
{
     // non-polymorphic type
};
 
int main()
{
    Derived1 obj1; // object1 created with type Derived1
    Derived2 obj2; // object2 created with type Derived2
 
    Base1& b1 = obj1; // b1 refers to the object obj1
    Base2& b2 = obj2; // b2 refers to the object obj2
 
    std::cout << "Expression type of b1: " << typeid(decltype(b1)).name() << '\n'
              << "Expression type of b2: " << typeid(decltype(b2)).name() << '\n'
              << "Object type of b1: " << typeid(b1).name() << '\n'
              << "Object type of b2: " << typeid(b2).name() << '\n'
              << "Size of b1: " << sizeof b1 << '\n'
              << "Size of b2: " << sizeof b2 << '\n';
}

Mögliche Ausgabe

Expression type of b1: Base1
Expression type of b2: Base2
Object type of b1: Derived1
Object type of b2: Base2
Size of b1: 8
Size of b2: 1

[bearbeiten] Strikte Aliasing-Regel

Der Zugriff auf ein Objekt über einen Ausdruck eines anderen Typs als des Typs, mit dem es erstellt wurde, führt in vielen Fällen zu undefiniertem Verhalten; siehe reinterpret_cast für die Liste der Ausnahmen und Beispiele.

[bearbeiten] Ausrichtung

Jeder Objekttyp hat die Eigenschaft Ausrichtungsanforderung, die ein nicht-negativer ganzzahliger Wert ist (vom Typ std::size_t und immer eine Zweierpotenz), der die Anzahl der Bytes zwischen aufeinanderfolgenden Adressen repräsentiert, an denen Objekte dieses Typs alloziert werden können.

Die Ausrichtungsanforderung eines Typs kann mit alignof oder std::alignment_of abgefragt werden. Die Zeiger-Ausrichtungsfunktion std::align kann verwendet werden, um einen geeignet ausgerichteten Zeiger innerhalb eines Puffers zu erhalten. std::aligned_storage kann verwendet werden, um geeignet ausgerichteten Speicher zu erhalten.(bis C++23)

(seit C++11)

Jeder Objekttyp erzwingt seine Ausrichtungsanforderung an jedes Objekt dieses Typs; eine strengere Ausrichtung (mit größerer Ausrichtungsanforderung) kann mit alignas angefordert werden(seit C++11). Der Versuch, ein Objekt in Speicher zu erstellen, der die Ausrichtungsanforderungen des Objekttyps nicht erfüllt, führt zu undefiniertem Verhalten.

Um die Ausrichtungsanforderungen aller nicht-statischen Member einer Klasse zu erfüllen, können Füllbits nach einigen ihrer Member eingefügt werden.

#include <iostream>
 
// objects of type S can be allocated at any address
// because both S.a and S.b can be allocated at any address
struct S
{
    char a; // size: 1, alignment: 1
    char b; // size: 1, alignment: 1
}; // size: 2, alignment: 1
 
// objects of type X must be allocated at 4-byte boundaries
// because X.n must be allocated at 4-byte boundaries
// because int's alignment requirement is (usually) 4
struct X
{
    int n;  // size: 4, alignment: 4
    char c; // size: 1, alignment: 1
    // three bytes of padding bits
}; // size: 8, alignment: 4 
 
int main()
{
    std::cout << "alignof(S) = " << alignof(S) << '\n'
              << "sizeof(S)  = " << sizeof(S) << '\n'
              << "alignof(X) = " << alignof(X) << '\n'
              << "sizeof(X)  = " << sizeof(X) << '\n';
}

Mögliche Ausgabe

alignof(S) = 1
sizeof(S)  = 2
alignof(X) = 4
sizeof(X)  = 8

Die schwächste Ausrichtung (die kleinste Ausrichtungsanforderung) ist die Ausrichtung von char, signed char und unsigned char, die gleich 1 ist; die größte fundamentale Ausrichtung jedes Typs ist implementierungsabhängig und gleich der Ausrichtung von std::max_align_t(seit C++11).

Fundamentale Ausrichtungen werden für Objekte aller Arten von Speicherdauern unterstützt.

Wenn die Ausrichtung eines Typs strenger (größer) als std::max_align_t mittels alignas gemacht wird, wird dies als Typ mit erweiterter Ausrichtungsanforderung bezeichnet. Ein Typ, dessen Ausrichtung erweitert ist, oder ein Klassentyp, dessen nicht-statisches Datenelement eine erweiterte Ausrichtung hat, ist ein über-ausgerichteter Typ.

Allocator-Typen müssen über-ausgerichtete Typen korrekt behandeln.

(seit C++11)


Es ist implementierungsabhängig, ob new-Ausdrücke und(bis C++17) std::get_temporary_buffer über-ausgerichtete Typen unterstützen.

(seit C++11)
(bis C++20)

[bearbeiten] Anmerkungen

Objekte in C++ haben eine andere Bedeutung als Objekte in der objektorientierten Programmierung (OOP)

Objekte in C++ Objekte in OOP
können jeden Objekttyp haben
(siehe std::is_object)
müssen einen Klassentyp haben
kein Konzept von "Instanz" haben das Konzept der "Instanz" (und es gibt Mechanismen wie instanceof, um Beziehungen vom Typ "Instanz-von" zu erkennen)
kein Konzept von "Schnittstelle" haben das Konzept der "Schnittstelle" (und es gibt Mechanismen wie instanceof, um zu erkennen, ob eine Schnittstelle implementiert ist)
Polymorphismus muss explizit über virtuelle Member aktiviert werden Polymorphismus ist immer aktiviert

Im Fehlerbericht P0593R6 wurde die implizite Objekterzeugung als stattfindend betrachtet, wenn ein Byte-Array erstellt oder eine Allokationsfunktion aufgerufen wird (die möglicherweise benutzerdefiniert und constexpr ist) während der konstanten Auswertung. Solche Zulassungen führten jedoch zu Indeterminismus bei der konstanten Auswertung, was unerwünscht und in gewisser Hinsicht nicht implementierbar war. Daher P2747R2 verbietet eine solche implizite Objekterzeugung in der konstanten Auswertung. Wir behandeln eine solche Änderung als Fehlerbericht, obwohl das gesamte Papier dies nicht tut.

[bearbeiten] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 633 C++98 Variablen konnten nur Objekte sein sie können auch Referenzen sein
CWG 734 C++98 es war nicht spezifiziert, ob definierte Variablen
im selben Gültigkeitsbereich, die garantiert
denselben Wert haben, dieselbe Adresse haben können
Adresse ist garantiert
unterschiedlich, wenn ihre Lebensdauern sich überlappen,
unabhängig von ihren Werten
CWG 1189 C++98 zwei Basisklassen-Unterobjekte desselben
Typs dieselbe Adresse haben könnten
sie haben immer
unterschiedliche Adressen
CWG 1861 C++98 bei übergroßen Bitfeldern von schmalen Zeichen-
Typen, alle Bits der Objektdarstellung
beteiligten sich immer noch an der Wertdarstellung
erlaubt Füllbits
CWG 2489 C++98 char[] können keinen Speicher bereitstellen, aber Objekte
könnten implizit in seinem Speicher erstellt werden
Objekte können nicht implizit erstellt werden
innerhalb des Speichers von char[]
CWG 2519 C++98 die Definition der Objektdarstellung bezog sich nicht auf Bitfelder bezieht sich auf Bitfelder
CWG 2719 C++98 das Verhalten beim Erstellen eines Objekts
in nicht ausgerichteten Speicher war unklar
Das Verhalten ist
in diesem Fall nicht definiert.
CWG 2753 C++11 es war unklar, ob ein Basisspeicherarray eines
Initialisierungsliste Speicher mit einem String-Literal teilen kann
sie können Speicher teilen
CWG 2795 C++98 bei der Bestimmung, ob zwei Objekte mit überlappenden
Lebensdauern dieselbe Adresse haben können, wenn eines von ihnen ein
Unterobjekt der Größe Null ist, könnten sie ähnliche unterschiedliche Typen haben
erlaubt nur nicht-ähnliche Typen
P0593R6 C++98 das vorherige Objektmodell unterstützte viele
nützliche Idiome, die von der Standardbibliothek benötigt wurden
und war nicht kompatibel mit effektiven Typen in C
implizite Objekterzeugung hinzugefügt

[bearbeiten] Siehe auch

C-Dokumentation für Objekt