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

Les programmes C++ créent, détruisent, référencent, accèdent et manipulent des objets .

Un objet, en C++, a

Les entités suivantes ne sont pas des objets : valeur, référence, fonction, énumérateur, type, membre non statique de classe, template, spécialisation de template de classe ou de fonction, espace de noms, pack de paramètres, et this .

Une variable est un objet ou une référence qui n'est pas un membre de données non statique, qui est introduit par une déclaration .

Table des matières

Création d'objets

Les objets peuvent être explicitement créés par des définitions , new expressions , throw expressions , en modifiant le membre actif d'une union et en évaluant des expressions qui nécessitent des objets temporaires . L'objet créé est unique dans la création explicite d'objet.

Les objets de types à durée de vie implicite peuvent également être créés implicitement par

  • sauf pendant l'évaluation constante, les opérations qui commencent la durée de vie d'un tableau de type unsigned char ou std::byte (depuis C++17) , auquel cas de tels objets sont créés dans le tableau,
  • appel aux fonctions d'allocation suivantes, auquel cas de tels objets sont créés dans le stockage alloué :
(depuis C++17)
  • appel aux fonctions de copie de la représentation d'objet suivantes, auquel cas de tels objets sont créés dans la région de stockage de destination ou le résultat :
**Note:** Le contenu HTML n'a pas été traduit car : - Les balises HTML et attributs sont préservés comme demandé - Les termes C++ (`std::memcpy`, `std::memmove`) ne sont pas traduits - Il n'y avait pas de texte visible à traduire en dehors des termes techniques C++ La structure HTML reste identique à l'original.
(depuis C++20)
  • appel aux fonctions spécifiques suivantes, auquel cas de tels objets sont créés dans la région de stockage spécifiée :
  • std::start_lifetime_as
  • std::start_lifetime_as_array
(depuis C++23)

Zéro ou plusieurs objets peuvent être créés dans la même région de mémoire, tant que cela donne un comportement défini au programme. Si une telle création est impossible, par exemple en raison d'opérations conflictuelles, le comportement du programme est indéfini. Si plusieurs ensembles de tels objets implicitement créés donneraient un comportement défini au programme, il n'est pas spécifié quel ensemble d'objets est créé. En d'autres termes, les objets implicitement créés ne sont pas requis d'être définis de manière unique.

Après avoir implicitement créé des objets dans une région de stockage spécifiée, certaines opérations produisent un pointeur vers un objet créé approprié . L'objet créé approprié possède la même adresse que la région de stockage. De même, le comportement n'est indéfini que si aucune valeur de pointeur de ce type ne peut donner un comportement défini au programme, et il n'est pas spécifié quelle valeur de pointeur est produite s'il existe plusieurs valeurs donnant un comportement défini au programme.

#include <cstdlib>
struct X { int a, b; };
X* MakeX()
{
    // Un des comportements définis possibles :
    // l'appel à std::malloc crée implicitement un objet de type X
    // et ses sous-objets a et b, et retourne un pointeur vers cet objet X
    X* p = static_cast<X*>(std::malloc(sizeof(X)));
    p->a = 1;
    p->b = 2;
    return p;
}

Appel à std::allocator::allocate ou aux fonctions membres spéciales de copie/déplacement implicitement définies des types union peut également créer des objets.

Représentation d'objet et représentation de valeur

Certains types et objets ont des représentations d'objet et des représentations de valeur , elles sont définies dans le tableau ci-dessous :

Entité Représentation objet Représentation valeur
un type objet complet T la séquence de N unsigned char occupée par un objet complet de type T non- champ de bits , où N vaut sizeof ( T ) l'ensemble des bits dans la représentation objet de T qui participent à la représentation d'une valeur de type T
un objet complet non-champ de bits obj de type T les octets de obj correspondant à la représentation objet de T les bits de obj correspondant à la représentation valeur de T
un objet champ de bits bf la séquence de N bits occupée par bf , où N est la largeur du champ de bits l'ensemble des bits dans la représentation objet de bf qui participent à la représentation de la valeur de bf

Les bits dans la représentation objet d'un type ou d'un objet qui ne font pas partie de la représentation valeur sont des bits de remplissage .

Pour les types TriviallyCopyable , la représentation de la valeur fait partie de la représentation de l'objet, ce qui signifie que la copie des octets occupés par l'objet en mémoire est suffisante pour produire un autre objet avec la même valeur (sauf si l'objet est un sous-objet potentiellement chevauchant, ou si la valeur est une représentation piégée de son type et que son chargement dans le CPU déclenche une exception matérielle, telle que les valeurs flottantes SNaN ("signalling not-a-number") ou les entiers NaT ("not-a-thing")).

Bien que la plupart des implémentations n'autorisent pas les représentations piégées, les bits de remplissage, ou les représentations multiples pour les types entiers, il existe des exceptions ; par exemple, une valeur d'un type entier sur Itanium peut être une représentation piégée .

L'inverse n'est pas nécessairement vrai : deux objets d'un type TriviallyCopyable avec des représentations objet différentes peuvent représenter la même valeur. Par exemple, plusieurs motifs binaires de nombres à virgule flottante représentent la même valeur spéciale NaN . Plus communément, des bits de remplissage peuvent être introduits pour satisfaire les exigences d'alignement , les tailles des champs de bits , etc.

#include <cassert>
struct S
{
    char c;  // valeur de 1 octet
             // 3 octets de bits de remplissage (en supposant alignof(float) == 4)
    float f; // valeur de 4 octets (en supposant sizeof(float) == 4)
    bool operator==(const S& arg) const // égalité basée sur la valeur
    {
        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'; // modifier certains bits de remplissage
    assert(s1 == s2); // la valeur n'a pas changé
}

Pour les objets de type char , signed char , et unsigned char (sauf s'ils sont des champs de bits surdimensionnés), chaque bit de la représentation de l'objet doit participer à la représentation de la valeur et chaque motif de bits possible représente une valeur distincte (aucun bit de remplissage, bit piégé ou représentation multiple autorisé).

Sous-objets

Un objet peut avoir des sous-objets . Ceux-ci incluent

  • objets membres
  • sous-objets de classe de base
  • éléments de tableau

Un objet qui n'est pas un sous-objet d'un autre objet est appelé objet complet .

Si un objet complet, un sous-objet membre ou un élément de tableau est de type classe , son type est considéré comme la classe la plus dérivée , pour le distinguer du type classe de tout sous-objet de classe de base. Un objet de type classe la plus dérivée ou de type non-classe est appelé objet le plus dérivé .

Pour une classe,

sont appelés ses sous-objets potentiellement construits .

Taille

Un sous-objet est un sous-objet potentiellement chevauchant s'il s'agit d'un sous-objet de classe de base ou d'un membre de données non statique déclaré avec l'attribut [[ no_unique_address ]] (depuis C++20) .

Un objet obj ne peut possiblement avoir une taille nulle que si toutes les conditions suivantes sont satisfaites :

  • obj est un sous-objet potentiellement chevauchant.
  • obj est d'un type classe sans fonctions membres virtuelles et classes de base virtuelles.
  • obj n'a aucun sous-objet de taille non nulle ou champs de bits sans nom de longueur non nulle.

Pour un objet obj satisfaisant toutes les conditions ci-dessus :

  • Si obj est un sous-objet de classe de base d'un type de classe standard-layout (depuis C++11) sans membres de données non statiques, il a une taille nulle.
  • Sinon, il est défini par l'implémentation dans quelles circonstances obj a une taille nulle.

Voir l'optimisation des classes de base vides pour plus de détails.

Tout objet non-champ de bits de taille non nulle doit occuper un ou plusieurs octets de stockage, incluant chaque octet occupé (en totalité ou en partie) par l'un de ses sous-objets. Le stockage occupé doit être contigu si l'objet est de type trivialement copiable ou de disposition standard (depuis C++11) .

Adresse

Sauf si un objet est un champ de bits ou un sous-objet de taille nulle, l' adresse de cet objet est l'adresse du premier byte qu'il occupe.

Un objet peut contenir d'autres objets, auquel cas les objets contenus sont imbriqués dans l'objet précédent. Un objet a est imbriqué dans un autre objet b si l'une des conditions suivantes est satisfaite :

  • a est un sous-objet de b .
  • b fournit le stockage pour a .
  • Il existe un objet c a est imbriqué dans c , et c est imbriqué dans b .

Un objet est un objet potentiellement non unique s'il s'agit de l'un des objets suivants :

(depuis C++11)
  • Un sous-objet d'un objet potentiellement non unique.

Pour deux objets non-champs de bits ayant des durées de vie qui se chevauchent :

  • Si l'une des conditions suivantes est satisfaite, elles peuvent avoir la même adresse :
  • L'un d'eux est imbriqué dans l'autre.
  • L'un d'eux est un sous-objet de taille zéro, et leurs types ne sont pas similaires .
  • Ils sont tous deux des objets potentiellement non uniques.
  • Sinon, ils ont toujours des adresses distinctes et occupent des octets de stockage disjoints.
// les littéraux de caractères sont toujours uniques
static const char test1 = 'x';
static const char test2 = 'x';
const bool b = &test1 != &test2;      // toujours vrai
// le caractère 'x' accessible depuis « r », « s » et « il »
// peut avoir la même adresse (c.-à-d. ces objets peuvent partager le stockage)
static const char (&r) [] = "x";
static const char *s = "x";
static std::initializer_list<char> il = {'x'};
const bool b2 = r != il.begin();      // résultat non spécifié
const bool b3 = r != s;               // résultat non spécifié
const bool b4 = il.begin() != &test1; // toujours vrai
const bool b5 = r != &test1;          // toujours vrai

Objets polymorphes

Les objets d'un type de classe qui déclare ou hérite d'au moins une fonction virtuelle sont des objets polymorphes. Dans chaque objet polymorphe, l'implémentation stocke des informations supplémentaires (dans toutes les implémentations existantes, il s'agit d'un pointeur sauf s'il est optimisé), qui sont utilisées par les appels de fonction virtuelle et par les fonctionnalités RTTI ( dynamic_cast et typeid ) pour déterminer, à l'exécution, le type avec lequel l'objet a été créé, indépendamment de l'expression dans laquelle il est utilisé.

Pour les objets non-polymorphiques, l'interprétation de la valeur est déterminée à partir de l'expression dans laquelle l'objet est utilisé, et est décidée au moment de la compilation.

#include <iostream>
#include <typeinfo>
struct Base1
{
    // type polymorphe : déclare un membre virtuel
    virtual ~Base1() {}
};
struct Derived1 : Base1
{
     // type polymorphe : hérite d'un membre virtuel
};
struct Base2
{
     // type non polymorphe
};
struct Derived2 : Base2
{
     // type non polymorphe
};
int main()
{
    Derived1 obj1; // objet1 créé avec le type Derived1
    Derived2 obj2; // objet2 créé avec le type Derived2
    Base1& b1 = obj1; // b1 fait référence à l'objet obj1
    Base2& b2 = obj2; // b2 fait référence à l'objet 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';
}

Sortie possible :

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

Aliasing strict

Accéder à un objet en utilisant une expression d'un type différent de celui avec lequel il a été créé est un comportement indéfini dans de nombreux cas, consultez reinterpret_cast pour la liste des exceptions et des exemples.

Alignement

Chaque type d'objet possède une propriété appelée exigence d'alignement , qui est une valeur entière non négative (de type std::size_t , et toujours une puissance de deux) représentant le nombre d'octets entre les adresses successives auxquelles les objets de ce type peuvent être alloués.

L'exigence d'alignement d'un type peut être interrogée avec alignof ou std::alignment_of . La fonction d'alignement de pointeur std::align peut être utilisée pour obtenir un pointeur correctement aligné dans un tampon. std::aligned_storage peut être utilisé pour obtenir un stockage correctement aligné. (jusqu'à C++23)

(depuis C++11)

Chaque type d'objet impose son exigence d'alignement sur chaque objet de ce type ; un alignement plus strict (avec une exigence d'alignement plus grande) peut être demandé en utilisant alignas (depuis C++11) . Tenter de créer un objet dans un espace mémoire qui ne respecte pas les exigences d'alignement du type de l'objet est un comportement indéfini.

Afin de satisfaire les exigences d'alignement de tous les membres non statiques d'une classe , des bits de remplissage peuvent être insérés après certains de ses membres.

#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';
}

Sortie possible :

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

L'alignement le plus faible (la plus petite exigence d'alignement) est l'alignement de char , signed char , et unsigned char , qui est égal à 1 ; le plus grand alignement fondamental de tout type est défini par l'implémentation et égal à l'alignement de std::max_align_t (depuis C++11) .

Les alignements fondamentaux sont pris en charge pour les objets de toutes les durées de stockage.

Si l'alignement d'un type est rendu plus strict (plus grand) que std::max_align_t en utilisant alignas , il est connu comme un type ayant une exigence d' alignement étendu . Un type dont l'alignement est étendu ou un type de classe dont un membre de données non statique a un alignement étendu est un type sur-aligné .

Allocator doivent gérer correctement les types sur-alignés.

(depuis C++11)


Il est défini par l'implémentation si new expressions et (jusqu'en C++17) std::get_temporary_buffer prennent en charge les types sur-alignés.

(depuis C++11)
(jusqu'en C++20)

Notes

Les objets en C++ ont une signification différente des objets dans la programmation orientée objet (POO) :

Objets en C++ Objets en POO
peuvent avoir n'importe quel type d'objet
(voir std::is_object )
doivent avoir un type classe
pas de concept d'« instance » possèdent le concept d'« instance » (et il existe des mécanismes comme instanceof pour détecter la relation « instance-de »)
pas de concept d'« interface » possèdent le concept d'« interface » (et il existe des mécanismes comme instanceof pour détecter si une interface est implémentée)
le polymorphisme doit être explicitement activé via des membres virtuels le polymorphisme est toujours activé

Dans le rapport de défaut P0593R6 , la création implicite d'objet était considérée comme se produisant lors de la création d'un tableau d'octets ou de l'invocation d'une fonction d'allocation (qui est potentiellement définie par l'utilisateur et constexpr ) pendant l'évaluation constante. Cependant, cette autorisation a causé un indéterminisme dans l'évaluation constante qui était indésirable et impossible à implémenter sous certains aspects. Par conséquent, P2747R2 a interdit une telle création implicite d'objet dans l'évaluation constante. Nous traitons intentionnellement ce changement comme un rapport de défaut bien que l'ensemble du document ne le soit pas.

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Appliqué à Comportement publié Comportement corrigé
CWG 633 C++98 les variables ne pouvaient être que des objets elles peuvent aussi être des références
CWG 734 C++98 il n'était pas spécifié si les variables définies
dans la même portée qui sont garanties d'avoir
la même valeur peuvent avoir la même adresse
l'adresse est garantie d'être
différente si leurs durées de vie se chevauchent,
indépendamment de leurs valeurs
CWG 1189 C++98 deux sous-objets de classe de base du même
type pouvaient avoir la même adresse
ils ont toujours
des adresses distinctes
CWG 1861 C++98 pour les champs de bits surdimensionnés de types caractères
étroits, tous les bits de la représentation d'objet
participaient encore à la représentation de valeur
permet des bits de remplissage
CWG 2489 C++98 char [ ] ne peut pas fournir de stockage, mais les objets
pouvaient être implicitement créés dans son stockage
les objets ne peuvent pas être implicitement créés
dans le stockage de char [ ]
CWG 2519 C++98 la définition de la représentation d'objet ne traitait pas des champs de bits traite des champs de bits
CWG 2719 C++98 le comportement de création d'un objet
dans un stockage non aligné n'était pas clair
le comportement est
indéfini dans ce cas
CWG 2753 C++11 il n'était pas clair si un tableau de support d'une
liste d'initialisation peut partager le stockage avec un littéral de chaîne
ils peuvent partager le stockage
CWG 2795 C++98 lors de la détermination si deux objets avec des durées de vie
se chevauchant peuvent avoir la même adresse, si l'un d'eux est un
sous-objet de taille zéro, ils pouvaient avoir des types distincts similaires
permet seulement des types non similaires
P0593R6 C++98 le modèle d'objet précédent ne supportait pas de nombreux
idiomes utiles requis par la bibliothèque standard
et n'était pas compatible avec les types effectifs en C
création d'objet implicite ajoutée

Voir aussi

Documentation C pour Object