Namespaces
Variants

Object

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

C++-Programme erstellen, zerstören, referenzieren, greifen zu und manipulieren Objekte .

Ein Objekt, in C++, hat

Die folgenden Entitäten sind keine Objekte: Wert, Referenz, Funktion, Enumerator, Typ, nicht-statisches Klassenmitglied, Template, Klassen- oder Funktions-Template-Spezialisierung, Namensbereich, Parameterpaket und this .

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

Inhaltsverzeichnis

Objekterstellung

Objekte können explizit durch Definitionen , new -Ausdrücke , throw -Ausdrücke , das Ändern des aktiven Members eines Unions und die Auswertung von Ausdrücken, die temporäre Objekte erfordern, erstellt werden. Das erstellte Objekt ist bei der expliziten Objekterstellung eindeutig definiert.

Objekte von implicit-lifetime types können auch implizit erstellt werden durch

  • außer während der Konstantenauswertung, Operationen, die die Lebensdauer eines Arrays vom Typ unsigned char oder std::byte (since C++17) , in welchem Fall solche Objekte im Array erzeugt werden,
  • Aufruf der folgenden allozierenden Funktionen, in welchem Fall solche Objekte im allokierten Speicher erzeugt werden:
(seit C++17)
  • Aufruf der folgenden Objektrepräsentation Kopierfunktionen, wobei solche Objekte im Zielspeicherbereich oder dem Ergebnis erstellt werden:
(seit C++20)
  • Aufruf der folgenden spezifischen Funktionen, in welchem Fall solche Objekte im angegebenen Speicherbereich erzeugt werden:
  • std::start_lifetime_as
  • std::start_lifetime_as_array
(since C++23)

Null oder mehr Objekte können in derselben Speicherregion erzeugt werden, solange dies dem Programm definiertes Verhalten verleiht. Falls eine solche Erzeugung unmöglich ist, z.B. aufgrund konfligierender Operationen, ist das Verhalten des Programms undefiniert. Falls mehrere solcher Sätze implizit erzeugter Objekte dem Programm definiertes Verhalten verleihen würden, ist nicht spezifiziert, welcher dieser Objektsätze erzeugt wird. Mit anderen Worten: implizit erzeugte Objekte müssen nicht eindeutig definiert sein.

Nach dem impliziten Erstellen von Objekten innerhalb eines bestimmten Speicherbereichs erzeugen einige Operationen einen Zeiger auf ein suitable created object . Das suitable created object hat dieselbe Adresse wie der Speicherbereich. Ebenso ist das Verhalten nur dann undefiniert, wenn kein solcher Zeigerwert dem Programm definiertes Verhalten geben kann, und es ist nicht spezifiziert, welcher Zeigerwert erzeugt wird, wenn mehrere Werte dem Programm definiertes Verhalten geben.

#include <cstdlib>
struct X { int a, b; };
X* MakeX()
{
    // Eines der möglichen definierten Verhaltensweisen:
    // Der Aufruf von std::malloc erstellt implizit ein Objekt vom Typ X
    // und seine Unterobjekte a und b, und gibt einen Zeiger auf dieses X-Objekt zurück
    X* p = static_cast<X*>(std::malloc(sizeof(X)));
    p->a = 1;
    p->b = 2;
    return p;
}

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

Objektdarstellung und Wertedarstellung

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

Entität Objektdarstellung Wertdarstellung
ein vollständiger Objekttyp T die Folge von N unsigned char Objekten, die von einem Bitfeld -freien vollständigen Objekt des Typs T belegt werden, wobei N gleich sizeof ( T ) ist die Menge der Bits in der Objektdarstellung von T , die an der Darstellung eines Werts des Typs T teilnehmen
ein Bitfeld-freies vollständiges Objekt obj des Typs T die Bytes von obj , die der Objektdarstellung von T entsprechen die Bits von obj , die der Wertdarstellung von T entsprechen
ein Bitfeld-Objekt bf die Folge von N Bits, die von bf belegt werden, wobei N die Breite des Bitfelds ist die Menge der Bits in der Objektdarstellung von bf , die an der Darstellung des Werts von bf teilnehmen

Bits in der Objektdarstellung eines Typs oder Objekts, die nicht Teil der Wertedarstellung sind, werden Padding-Bits genannt.

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 weiteres Objekt mit demselben Wert zu erzeugen (außer wenn das Objekt ein potenziell überlappendes Subobjekt ist oder der Wert eine Trap-Darstellung seines Typs ist und das Laden in die CPU eine Hardware-Ausnahme auslöst, wie etwa SNaN ("signalling not-a-number") Fließkommawerte oder NaT ("not-a-thing") Ganzzahlen).

Obwohl die meisten Implementierungen keine Trap-Darstellungen, Padding-Bits oder Mehrfachdarstellungen für Integer-Typen erlauben, gibt es Ausnahmen; zum Beispiel kann ein Wert eines Integer-Typs auf Itanium eine Trap-Darstellung sein .

Das Umgekehrte gilt nicht notwendigerweise: Zwei Objekte eines TriviallyCopyable -Typs mit unterschiedlichen Objektrepräsentationen können denselben Wert repräsentieren. Beispielsweise repräsentieren mehrere Gleitkomma-Bitmuster denselben speziellen Wert NaN . Häufiger können Füllbits eingefügt werden, um Ausrichtungsanforderungen , Bitfeld -Größen etc. zu erfüllen.

#include <cassert>
struct S
{
    char c;  // 1-Byte-Wert
             // 3 Bytes Füllbits (angenommen alignof(float) == 4)
    float f; // 4-Byte-Wert (angenommen sizeof(float) == 4)
    bool operator==(const S& arg) const // wertbasierte Gleichheit
    {
        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'; // einige Füllbits modifizieren
    assert(s1 == s2); // Wert hat sich nicht geändert
}

Für Objekte vom Typ char , signed char und unsigned char (sofern es sich nicht um übergroße Bitfelder handelt) muss jedes Bit der Objektdarstellung an der Wertdarstellung teilnehmen und jedes mögliche Bitmuster repräsentiert einen eindeutigen Wert (keine Padding-Bits, Trap-Bits oder Mehrfachdarstellungen erlaubt).

Subobjekte

Ein Objekt kann Subobjekte besitzen. Diese umfassen

  • Member-Objekte
  • Basisklassen-Subobjekte
  • 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 vom Klassentyp ist, wird sein Typ als am stärksten abgeleitete Klasse betrachtet, um ihn vom Klassentyp eines Basisklassenunterobjekts zu unterscheiden. Ein Objekt vom Typ einer am stärksten abgeleiteten Klasse oder eines Nicht-Klassentyps wird als am stärksten abgeleitetes Objekt bezeichnet.

Für eine Klasse,

werden als seine potenziell konstruierten Unterobjekte bezeichnet.

Größe

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

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

  • obj ist ein potenziell überlappendes Subobjekt.
  • obj ist von einem Klassentyp ohne virtuelle Memberfunktionen und virtuelle Basisklassen.
  • obj besitzt kein Subobjekt 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-Subobjekt eines Standard-Layout (seit C++11) Klassentyps ohne nicht-statische Datenelemente ist, hat es Nullgröße.
  • Andernfalls ist implementierungsdefiniert, unter welchen Umständen obj Nullgröße hat.

Siehe empty base optimization für weitere Details.

Jedes Nicht-Bitfeld-Objekt mit einer Größe ungleich Null muss einen oder mehrere Bytes an Speicher belegen, einschließlich jedes Bytes, das (vollständig oder teilweise) von einem seiner Unterobjekte belegt wird. Der belegte Speicher muss zusammenhängend sein, wenn das Objekt vom trivial kopierbaren oder Standard-Layout (since C++11) Typ ist.

Adresse

Sofern ein Objekt kein Bitfeld oder ein Unterobjekt von Nullgröße 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 innerhalb des ersteren Objekts geschachtelt. Ein Objekt a ist innerhalb eines anderen Objekts b geschachtelt, wenn eine der folgenden Bedingungen erfüllt ist:

  • a ist ein Unterobjekt von b .
  • b stellt Speicher bereit für a .
  • Es existiert ein Objekt c , in dem a innerhalb von c geschachtelt ist und c innerhalb von b geschachtelt 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 beliebige Nicht-Bitfeld-Objekte mit überlappenden Lebensdauern :

  • Wenn eine der folgenden Bedingungen erfüllt ist, können sie dieselbe Adresse haben:
  • Einer von ihnen ist innerhalb des anderen verschachtelt.
  • Einer von ihnen ist ein Unterobjekt mit Nullgröße, und ihre Typen sind nicht ähnlich .
  • Sie sind beide potenziell nicht-eindeutige Objekte.
  • Andernfalls haben sie stets unterschiedliche Adressen und belegen disjunkte Bytes des Speichers.
// Zeichenliterale sind immer eindeutig
static const char test1 = 'x';
static const char test2 = 'x';
const bool b = &test1 != &test2;      // immer true
// Das Zeichen 'x' aus "r", "s" und "il"
// kann dieselbe Adresse haben (d.h. diese Objekte können Speicher teilen)
static const char (&r) [] = "x";
static const char *s = "x";
static std::initializer_list<char> il = {'x'};
const bool b2 = r != il.begin();      // nicht spezifiziertes Ergebnis
const bool b3 = r != s;               // nicht spezifiziertes Ergebnis
const bool b4 = il.begin() != &test1; // immer true
const bool b5 = r != &test1;          // immer true

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 (in jeder existierenden Implementierung ist dies ein Zeiger, sofern nicht wegoptimiert), die von virtuellen Funktionsaufrufen und den RTTI-Features ( 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 durch den Ausdruck bestimmt, in dem das Objekt verwendet wird, und wird zur Kompilierzeit entschieden.

#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

Strict Aliasing

Das Zugreifen auf ein Objekt mit einem Ausdruck eines anderen Typs als dem, mit dem es erstellt wurde, ist in vielen Fällen undefiniertes Verhalten. Siehe reinterpret_cast für die Liste der Ausnahmen und Beispiele.

Ausrichtung

Jeder Objekttyp besitzt eine Eigenschaft namens Ausrichtungsanforderung , die einen nichtnegativen ganzzahligen Wert (vom Typ std::size_t , stets eine Zweierpotenz) darstellt und die Anzahl Bytes zwischen aufeinanderfolgenden Adressen angibt, an denen Objekte dieses Typs allokiert werden können.

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

(seit C++11)

Jeder Objekttyp stellt seine Ausrichtungsanforderung an jedes Objekt dieses Typs ; strengere Ausrichtung (mit größerer Ausrichtungsanforderung) kann mittels alignas angefordert werden (seit C++11) . Der Versuch, ein Objekt in Speicher zu erstellen, der nicht den Ausrichtungsanforderungen des Objekttyps entspricht, ist undefiniertes Verhalten.

Um die Ausrichtungsanforderungen aller nicht-statischen Mitglieder einer Klasse zu erfüllen, können Padding-Bits nach einigen ihrer Mitglieder 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 eines beliebigen Typs ist implementierungsdefiniert 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 mithilfe von alignas strenger (größer) gemacht wird als std::max_align_t , wird dies als Typ mit erweiterter Ausrichtung bezeichnet. Ein Typ mit erweiterter Ausrichtung oder ein Klassentyp, dessen nicht-statische Datenelemente eine erweiterte Ausrichtung haben, ist ein überausgerichteter Typ .

Allocator -Typen müssen überausgerichtete Typen korrekt verarbeiten.

(since C++11)


Es ist implementierungsdefiniert, ob new -Ausdrücke und (bis C++17) std::get_temporary_buffer überalignierte Typen unterstützen.

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

Hinweise

Objekte in C++ haben eine andere Bedeutung als Objekte in objektorientierter 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 von "Instanz" (und es gibt Mechanismen wie instanceof um "Instanz-von"-Beziehungen zu erkennen)
kein Konzept von "Interface" haben das Konzept von "Interface" (und es gibt Mechanismen wie instanceof um zu erkennen, ob ein Interface implementiert ist)
Polymorphie muss explizit durch virtuelle Member aktiviert werden Polymorphie ist immer aktiviert

Im Defektbericht P0593R6 wurde die implizite Objekterstellung ursprünglich als stattfindend betrachtet, wenn ein Byte-Array erstellt oder eine Allokationsfunktion aufgerufen wird (die möglicherweise benutzerdefiniert und constexpr ist). Jedoch verursachte diese Erlaubnis Nichtdeterminismus in der Konstantenauswertung, was unerwünscht und in manchen Aspekten nicht implementierbar war. Infolgedessen hat P2747R2 solche implizite Objekterstellung in der Konstantenauswertung untersagt. Wir behandeln diese Änderung bewusst als Defektbericht, obwohl das gesamte Dokument keiner ist.

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 Variablen im selben
Gültigkeitsbereich, die garantiert denselben Wert haben,
dieselbe Adresse haben können
Adresse ist garantiert unterschiedlich, wenn sich
ihre Lebensdauern überschneiden, unabhängig
von ihren Werten
CWG 1189 C++98 zwei Basisklassen-Subobjekte desselben Typs
könnten dieselbe Adresse haben
sie haben immer
unterschiedliche Adressen
CWG 1861 C++98 für überbreite Bitfelder schmaler Zeichentypen
nahmen alle Bits der Objektdarstellung noch
an der Wertdarstellung teil
erlaubt Padding-Bits
CWG 2489 C++98 char [ ] kann keinen Speicher bereitstellen, aber Objekte
konnten implizit in seinem Speicher erstellt werden
Objekte können nicht implizit im Speicher
von char [ ] erstellt werden
CWG 2519 C++98 die Definition der Objektdarstellung behandelte Bitfelder nicht behandelt Bitfelder
CWG 2719 C++98 das Verhalten bei der Erstellung eines Objekts
in nicht ausgerichtetem Speicher war unklar
das Verhalten ist in diesem Fall
undefiniert
CWG 2753 C++11 es war unklar, ob ein Backing-Array eines
Initialisierer-Liste Speicher mit einem String-Literal teilen kann
sie können Speicher teilen
CWG 2795 C++98 bei der Bestimmung, ob zwei Objekte mit sich überschneidenden
Lebensdauern dieselbe Adresse haben können, wenn eines davon
ein Subobjekt 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 nicht, die von der Standardbibliothek benötigt werden,
und war nicht kompatibel mit effektiven Typen in C
implizite Objekterstellung hinzugefügt

Siehe auch

C-Dokumentation für Object