web-dev-qa-db-fra.com

Quelles sont toutes les fonctions membres créées par le compilateur pour une classe? Cela se produit-il tout le temps?

Quelles sont toutes les fonctions membres créées par le compilateur pour une classe? Cela se produit-il tout le temps? comme destructeur. Ma préoccupation est de savoir si elle est créée pour toutes les classes, et pourquoi le constructeur par défaut est-il nécessaire?

58
Onnesh

C++ 98/03

S'ils sont nécessaires,

  1. le compilateur générera pour vous un constructeur par défaut à moins que vous ne déclariez un constructeur de votre choix.
  2. le compilateur générera un constructeur copie pour vous, sauf si vous déclarez le vôtre.
  3. le compilateur générera un copie opérateur d'affectation pour vous, sauf si vous déclarez le vôtre.
  4. le compilateur générera pour vous un destructeur à moins que vous ne déclariez le vôtre.

Comme l'a dit Péter dans un commentaire utile, tous ceux-ci ne sont générés que par le compilateur quand ils sont nécessaires. (La différence est que, lorsque le compilateur ne peut pas les créer, c'est Ok tant qu'ils ne sont pas utilisés.)


C++ 11

C++ 11 ajoute les règles suivantes, qui sont également valables pour C++ 14 (crédits à towi, voir ce commentaire ):

  • Le compilateur génère le constructeur move si
    • il n'y a pas de constructeur déclaré par l'utilisateur copier, et
    • il n'y a pas d'opérateur d'affectation déclaré par l'utilisateur copier, et
    • il n'y a pas d'opérateur d'affectation déclaré par l'utilisateur déplacer et
    • il n'y a pas de destructeur déclaré par l'utilisateur ,
    • il est pas marqué comme supprimé ,
    • et tous les membres et bases sont mobiles .
  • Similaire pour déplacer opérateur d'affectation : Il est généré s'il n'y a aucun utilisateur défini
    • il n'y a pas de constructeur déclaré par l'utilisateur copier, et
    • il n'y a pas d'opérateur d'affectation déclaré par l'utilisateur copier, et
    • il n'y a pas de constructeur déclaré par l'utilisateur déplacer et
    • il n'y a pas de destructeur déclaré par l'utilisateur ,
    • il est pas marqué comme supprimé ,
    • et tous les membres et bases sont mobiles .

Notez que ces règles sont un peu plus élaborées que les règles C++ 03 et ont plus de sens dans la pratique.

Pour une compréhension plus facile de ce qui est décrit ci-dessus, les éléments pour Thing:

class Thing {
public:
    Thing();                        // default constructor
    Thing(const Thing&);            // copy c'tor
    Thing& operator=(const Thing&); // copy-assign
    ~Thing();                       // d'tor
    // C++11:
    Thing(Thing&&);                 // move c'tor
    Thing& operator=(Thing&&);      // move-assign
};

Et comme lecture supplémentaire, si vous êtes un débutant en C++ - envisagez une conception qui ne vous oblige à implémenter aucun des cinq derniers, aka The Rule Of Zero (par Martinho Fernandes ).

84
sbi

Voulez-vous dire "défini" par "créé"?

$ 12.1 - "Le constructeur par défaut (12.1), le constructeur de copie et l'opérateur d'affectation de copie (12.8) et le destructeur (12.4) sont des fonctions membres spéciales.

Si "créé" signifie "défini", voici les parties importantes de la norme C++.

-Un constructeur par défaut déclaré implicitement pour une classe est implicitement défini lorsqu'il est utilisé pour créer un objet de son type de classe (1.8).

-Si une classe n'a pas de destructeur déclaré par l'utilisateur, un destructeur est déclaré implicitement. Un destructeur déclaré implicitement est défini implicitement lorsqu'il est utilisé pour détruire un objet de son type de classe.

-Si la définition de classe ne déclare pas explicitement un constructeur de copie, l'un est déclaré implicitement. Un constructeur de copie déclaré implicitement est défini implicitement s'il est utilisé pour initialiser un objet de son type de classe à partir d'une copie d'un objet de son type de classe ou d'un type de classe dérivé de son type de classe).

-Si la définition de classe ne déclare pas explicitement un opérateur d'affectation de copie, l'un est déclaré implicitement. Un opérateur d'affectation de copie déclaré implicitement est défini implicitement lorsqu'un objet de son type de classe se voit attribuer une valeur de son type de classe ou une valeur d'un type de classe dérivée de son type de classe.

2
Chubsdad

Par défaut, s'il n'est pas implémenté par l'utilisateur, le compilateur ajoute des fonctions membres à la classe. On les appelle les quatre grands:

  • constructeur par défaut
  • constructeur de copie
  • opérateur de copie (affectation)
  • destructeur

En fonction des types de membres et de la fonction membre répertoriée que vous vous fournissez, ceux-ci ne seront pas tous générés.

1
Klaim

D'autres réponses vous ont dit ce qui a été créé et que le compilateur ne peut les générer que s'il est utilisé.

Ma préoccupation est de savoir si elle est créée pour toutes les classes ...

Pourquoi inquiet? Vous pensez que cela crée du code indésirable dans l'exécutable? Peu probable, mais vous pouvez vérifier assez facilement avec votre environnement.

Ou peut-être votre souci était-il que cela ne crée pas un constructeur quand vous en voulez un? Rien à craindre ... ils sont toujours créés si nécessaire et non fournis par l'utilisateur.

... et pourquoi le constructeur par défaut est-il nécessaire?

Parce que les classes peuvent contenir des objets avec leurs propres destructeurs qui doivent être systématiquement invoqués. Par exemple, étant donné ...

struct X
{
    std::string a;
    std::string b;
};

... le destructeur par défaut s'assure que les destructeurs pour a et b fonctionnent.

0
Tony Delroy

Projet standard C++ 17 N4659

https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 6.1 "Déclarations et définitions" contient une note qui les résume probablement toutes:

3 [Remarque: dans certaines circonstances, les implémentations C++ définissent implicitement le constructeur par défaut (15.1), le constructeur de copie (15.8), le constructeur de déplacement (15.8), l'opérateur d'affectation de copie (15.8), l'opérateur d'affectation de déplacement (15.8) ou le destructeur ( 15.4) fonctions des membres. - note de fin] [Exemple: Étant donné

#include <string>

struct C {
  std::string s;         // std::string is the standard library class (Clause 24)
};

int main() {
  C a;
  C b = a;
  b = a;
}

l'implémentation définira implicitement des fonctions pour rendre la définition de C équivalente à

struct C {
  std::string s;
  C() : s() { }
  C(const C& x): s(x.s) { }
  C(C&& x): s(static_cast<std::string&&>(x.s)) { }
  // : s(std::move(x.s)) { }
  C& operator=(const C& x) { s = x.s; return *this; }
  C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; }
  // { s = std::move(x.s); return *this; }
  ~ C() { }
};

- fin exemple]

Les conditions dans lesquelles celles-ci sont déclarées sont expliquées à: Conditions pour la génération automatique du ctor par défaut/copier/déplacer et de l'opérateur d'affectation copier/déplacer?

Une bonne façon de s'assurer que quelque chose a un défaut est d'essayer de le faire utiliser = default comme expliqué à: Que signifie "par défaut" après la déclaration d'une fonction de classe?

L'exemple ci-dessous fait cela, et exerce également toutes les fonctions définies implicitement.

#include <cassert>
#include <string>

struct Default {
    int i;
    Default()                          = default;
    Default(const Default&)            = default;
    Default& operator=(Default&)       = default;
    Default& operator=(const Default&) = default;
    Default(Default&&)                 = default;
    Default& operator=(Default&&)      = default;
    ~Default()                         = default;
};

struct Instrument {
    int i;
    static std::string last_call;
    Instrument()                             { last_call = "ctor"; }
    Instrument(const Instrument&)            { last_call = "copy ctor"; }
    Instrument& operator=(Instrument&)       { last_call = "copy assign"; return *this; }
    Instrument& operator=(const Instrument&) { last_call = "copy assign const"; return *this; }
    Instrument(Instrument&&)                 { last_call = "move ctor";  }
    Instrument& operator=(Instrument&&)      { last_call = "move assign"; return *this; }
    ~Instrument()                            { last_call = "dtor"; }
};
std::string Instrument::last_call;

int main() {
    // See what the default constructors are doing.
    {
        // Default constructor.
        Default ctor;
        // i is uninitialized.
        // std::cout << ctor.i << std::endl;
        ctor.i = 1;

        // Copy constructor.
        Default copy_ctor(ctor);
        assert(copy_ctor.i = 1);

        // Copy assignment.
        Default copy_assign;
        copy_assign = ctor;
        assert(copy_assign.i = 1);

        // Copy assignment const.
        const Default const_ctor(ctor);
        Default copy_assign_const;
        copy_assign_const = const_ctor;
        assert(copy_assign_const.i == 1);

        // Move constructor.
        Default move_ctor(std::move(ctor));
        assert(move_ctor.i == 1);

        // Move assignment.
        Default move_assign;
        move_assign = std::move(ctor);
        assert(move_assign.i == 1);
    }

    // Check that the constructors are called by these calls.
    {
        // Default constructor.
        Instrument ctor;
        assert(Instrument::last_call == "ctor");

        // Copy constructor.
        Instrument copy_ctor(ctor);
        assert(Instrument::last_call == "copy ctor");

        // Copy assignment.
        copy_ctor = ctor;
        assert(Instrument::last_call == "copy assign");

        // Copy assignment const.
        const Instrument const_ctor(ctor);
        Instrument copy_assign_const;
        copy_assign_const = const_ctor;
        assert(Instrument::last_call == "copy assign const");

        // Move constructor.
        Instrument move_ctor(std::move(ctor));
        assert(Instrument::last_call == "move ctor");

        // Move assignment.
        Instrument move_assign;
        move_assign = std::move(ctor);
        assert(Instrument::last_call == "move assign");

        // Destructor.
        {
            Instrument dtor;
        }
        assert(Instrument::last_call == "dtor");
    }
}

GitHub en amont .

Testé avec GCC 7.3.0:

g++ -std=c++11 implicitly_defined.cpp