web-dev-qa-db-fra.com

La nouvelle fonctionnalité d'initialisation de membre C ++ 11 au niveau de la déclaration a-t-elle rendu les listes d'initialisation obsolètes?

Avec C++ 11, nous avons maintenant la possibilité d'initialiser les membres de la classe dans une déclaration d'en-tête:

class aClass
{
    private:
        int mInt{100};
    public:
         aClass();
        ~aClass();
};

Donc je suis un peu confus. Traditionnellement, les listes d'initialisation dans les constructeurs étaient utilisées pour l'initialisation des membres:

aClass::aClass()
: mInt(100)
{
    ...
}

La nouvelle fonctionnalité d'initialisation de membre C++ 11 au niveau de la déclaration a-t-elle rendu les listes d'initialisation obsolètes? Sinon, quels sont les avantages de l'un par rapport à l'autre? Quelles situations pourraient rendre l’initialisation à la déclaration avantageuse, ou les listes d’initialisation avantageuses? Quand faut-il utiliser l'un sur l'autre?

64
Vector

Non, ils ne sont pas obsolètes comme cet article Familiarisez-vous avec les nouveaux formulaires d'initialisation C++ 11 dit dans la Initialisation d'un membre de classe section ( l'emphase mienne ):

Gardez à l'esprit que si le même membre de données a à la fois un initialiseur de membre de classe et un mem-init dans le constructeur, ce dernier a la priorité. En fait, vous pouvez tirer parti de ce comportement en spécifiant une valeur par défaut pour un membre sous la forme d'un initialiseur de membre de classe qui sera utilisé si constructeur n'a pas un mem-init explicite pour ce membre. Sinon, mem-init du constructeur prendra effet en remplaçant l'initialiseur du membre de la classe. Cette technique est utile dans les classes ayant plusieurs constructeurs

Ainsi, bien que l’initialisation en classe soit un avantage pratique, elle ne supprime pas le besoin de listes d’initialisation, mais les deux fonctions fonctionnent ensemble pour vous permettre de spécifier des valeurs par défaut et de les remplacer en cas de besoin. Cela semble être aussi comment Bjarne Stroustrup le voit aussi, il dit:

Cela économise un peu de frappe, mais les avantages réels résident dans les classes avec plusieurs constructeurs. Souvent, tous les constructeurs utilisent un initialiseur commun pour un membre:

et fournit un exemple de membres qui ont un initialiseur commun:

class A {
  public:
    A(): a(7), b(5), hash_algorithm("MD5"), s("Constructor run") {}
    A(int a_val) : a(a_val), b(5), hash_algorithm("MD5"), s("Constructor run") {}
    A(D d) : a(7), b(g(d)), hash_algorithm("MD5"), s("Constructor run") {}
    int a, b;
  private:
    HashingFunction hash_algorithm;  // Cryptographic hash to be applied to all A instances
    std::string s;                   // String indicating state in object lifecycle
};

et dit:

Le fait que hash_algorithm et s ont chacun une valeur par défaut est perdu dans le fouillis de code et pourrait facilement devenir un problème lors de la maintenance. Au lieu de cela, nous pouvons factoriser l’initialisation des membres de données:

class A {
  public:
    A(): a(7), b(5) {}
    A(int a_val) : a(a_val), b(5) {}
    A(D d) : a(7), b(g(d)) {}
    int a, b;
  private:
    HashingFunction hash_algorithm{"MD5"};  // Cryptographic hash to be applied to all A instances
    std::string s{"Constructor run"};       // String indicating state in object lifecycle
};

Note: inconvénient en C++ 11

L'utilisation de l'initialisation dans C++ 11 des membres de classe présente un inconvénient, car elle fait d'une classe un élément non agrégé que nous ne pouvons plus utiliser initialisation d'agrégat , ce qui peut paraître assez surprenant. Ce n'est pas le cas dans C++ 14 où cette restriction a été supprimée. Voir: Initialisation d'agrégat C++ 11 pour les classes avec des initialiseurs de membres non statiques pour plus de détails.

68
Shafik Yaghmour

Non, ils ne sont pas obsolètes.

Les listes d'initialisation restent le seul moyen d'utiliser des arguments de constructeurs pour initialiser les membres de votre classe.

class A
{
 int a=7; //fine, give a default value
public:
 A();
};

class B
{
 int b; 
public:
 B(int arg) : b(arg) {}

 B(int arg, bool b) : b(arg) { ... }
};

Notez que si les deux sont présents, l'init du constructeur prendra effet en remplaçant l'initialisation du membre de la classe, ce qui est utile pour spécifier une valeur par défaut pour un membre de la classe.

7
quantdev

À mon sens, l’initialisation en classe est un enrichissement des listes mem-initializer. En C++ 03, les membres non répertoriés dans une liste mem-initializer-list étaient toujours initialisé par défaut. Cela signifie le constructeur par défaut pour les classes et aucune initialisation pour les types primitifs.

L’initialisation en classe vous permet simplement de spécifier vos propres valeurs par défaut. Il y a deux façons de voir les choses.

Un: si la plupart/tous les constructeurs de votre classe veulent fournir la même valeur initiale pour un membre, utilisez un initialiseur de classe pour ce membre. Pour les autres membres, utilisez mem-initializer-lists. Vous devrez bien sûr les utiliser chaque fois que la valeur initiale dépend des arguments du constructeur.

L'autre: fournir un initialiseur intégré à la classe pour tous les membres, exactement comment le constructeur par défaut de votre classe les initialisera. Ensuite, mem-initializer-lists, dans les constructeurs autres que ceux par défaut, récupère la sémantique "comment il se distingue d'un objet construit par défaut".

5
Angew