web-dev-qa-db-fra.com

Dans ce cas spécifique, existe-t-il une différence entre l'utilisation d'une liste d'initialisation de membre et l'attribution de valeurs dans un constructeur?

En interne et sur le code généré, y a-t-il vraiment une différence entre:

MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}

et

MyClass::MyClass()
{
  _capacity=15;
  _data=NULL;
  _len=0
}

merci...

88
Stef

En supposant que ces valeurs sont des types primitifs, alors non, il n'y a pas de différence. Les listes d'initialisation ne font la différence que lorsque vous avez des objets en tant que membres, car au lieu d'utiliser l'initialisation par défaut suivie d'une affectation, la liste d'initialisation vous permet d'initialiser l'objet à sa valeur finale. Cela peut être sensiblement plus rapide.

59
templatetypedef

Vous devez utiliser la liste d'initialisation pour initialiser les membres constants, les références et la classe de base

Lorsque vous devez initialiser un membre constant, des références et passer des paramètres aux constructeurs de classe de base, comme mentionné dans les commentaires, vous devez utiliser la liste d'initialisation.

struct aa
{
    int i;
    const int ci;       // constant member

    aa() : i(0) {} // will fail, constant member not initialized
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0) { ci = 3;} // will fail, ci is constant
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0), ci(3) {} // works
};

L'exemple (non exhaustif) de classe/struct contient une référence:

struct bb {};

struct aa
{
    bb& rb;
    aa(bb& b ) : rb(b) {}
};

// usage:

bb b;
aa a(b);

Et exemple d'initialisation de la classe de base qui nécessite un paramètre (par exemple, aucun constructeur par défaut):

struct bb {};

struct dd
{
    char c;
    dd(char x) : c(x) {}
};

struct aa : dd
{
    bb& rb;
    aa(bb& b ) : dd('a'), rb(b) {}
};
77
stefanB

Oui. Dans le premier cas, vous pouvez déclarer _capacity, _data et _len comme constantes:

class MyClass
{
private:
    const int _capacity;
    const void *_data;
    const int _len;
// ...
};

Cela serait important si vous souhaitez garantir const- la nessité de ces variables d'instance lors du calcul de leurs valeurs au moment de l'exécution, par exemple:

MyClass::MyClass() :
    _capacity(someMethod()),
    _data(someOtherMethod()),
    _len(yetAnotherMethod())
{
}

const instances doit être initialisé dans la liste d'initialisation o les types sous-jacents doivent fournir des constructeurs publics sans paramètres (ce que font les types primitifs).

18
Richard Cook

Je pense que ce lien http://www.cplusplus.com/forum/articles/17820/ donne une excellente explication - en particulier pour ceux qui découvrent C++.

La raison pour laquelle les listes d'initialiseurs sont plus efficaces est que dans le corps du constructeur, seules les affectations ont lieu, pas l'initialisation. Donc, si vous avez affaire à un type non intégré, le constructeur par défaut de cet objet a déjà été appelé avant que le corps du constructeur ne soit entré. À l'intérieur du corps du constructeur, vous affectez une valeur à cet objet.

En effet, il s'agit d'un appel au constructeur par défaut suivi d'un appel à l'opérateur d'affectation de copie. La liste d'initialisation vous permet d'appeler directement le constructeur de copie, ce qui peut parfois être beaucoup plus rapide (rappelez-vous que la liste d'initialisation est avant le corps du constructeur)

6
user929404

J'ajouterai que si vous avez des membres de type classe sans constructeur par défaut disponible, l'initialisation est le seul moyen de construire votre classe.

5
Fred Larson

Une grande différence est que l'affectation peut initialiser les membres d'une classe parent; l'initialiseur ne fonctionne que sur les membres déclarés dans la portée de classe actuelle.

3
Mark Ransom

Dépend des types impliqués. La différence est similaire entre

std::string a;
a = "hai";

et

std::string a("hai");

où la deuxième forme est la liste d'initialisation, c'est-à-dire que cela fait une différence si le type nécessite des arguments de constructeur ou est plus efficace avec des arguments de constructeur.

2
Puppy

La vraie différence se résume à la façon dont le compilateur gcc génère le code machine et fixe la mémoire. Explique:

  • (phase1) Avant le corps init (y compris la liste init): le compilateur alloue la mémoire requise pour la classe. La classe est déjà vivante!
  • (phase2) Dans le corps init: puisque la mémoire est allouée, chaque affectation indique désormais une opération sur la variable déjà existante/'initialisée'.

Il existe certainement d'autres façons de gérer les membres de type const. Mais pour leur faciliter la vie, les rédacteurs du compilateur gcc décident de mettre en place quelques règles

  1. les membres de type const doivent être initialisés avant le corps init.
  2. Après la phase 1, toute opération d'écriture n'est valide que pour les membres non constants.
1
lukmac

Il n'y a qu'une seule façon de initialiser les instances de classe de base et les variables membres non statiques et qui utilise la liste d'initialisation.

Si vous ne spécifiez pas de variable membre de base ou non statique dans la liste d'initialisation de votre constructeur, ce membre ou cette base sera initialisé par défaut (si le membre/base est un type de classe non-POD ou un tableau de classe non-POD types) ou laissé non initialisé autrement.

Une fois le corps du constructeur entré, toutes les bases ou tous les membres auront été initialisés ou laissés non initialisés (c'est-à-dire qu'ils auront une valeur indéterminée). Il n'y a aucune possibilité dans le corps du constructeur d'influencer la façon dont ils devraient être initialisés.

Vous pouvez peut-être attribuer de nouvelles valeurs aux membres dans le corps du constructeur, mais il n'est pas possible d'affecter aux membres const ou aux membres de type classe qui ont été rendus non assignables et il n'est pas possible de lier de nouveau les références.

Pour les types intégrés et certains types définis par l'utilisateur, l'affectation dans le corps du constructeur peut avoir exactement le même effet que l'initialisation avec la même valeur dans la liste d'initialisation.

Si vous ne parvenez pas à nommer un membre ou une base dans une liste d'initialisation et que cette entité est une référence, a un type de classe sans constructeur par défaut accessible déclaré par l'utilisateur, est qualifiée const et a un type POD ou est un type de classe POD ou un tableau de type classe POD contenant un membre qualifié const (directement ou indirectement), le programme est mal formé.

1
CB Bailey

Il existe une différence entre la liste d'initialisation et l'instruction d'initialisation dans un constructeur. Prenons le code ci-dessous:

#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>

class MyBase {
public:
    MyBase() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

class MyClass : public MyBase {
public:
    MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
        std::cout << __FUNCTION__ << std::endl;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

class MyClass2 : public MyBase {
public:
    MyClass2::MyClass2() {
        std::cout << __FUNCTION__ << std::endl;
        _capacity = 15;
        _data = NULL;
        _len = 0;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

int main() {
    MyClass c;
    MyClass2 d;

    return 0;
}

Lorsque MyClass est utilisé, tous les membres seront initialisés avant l'exécution de la première instruction dans un constructeur.

Mais, lorsque MyClass2 est utilisé, tous les membres ne sont pas initialisés lors de l'exécution de la première instruction dans un constructeur.

Dans un cas ultérieur, il peut y avoir un problème de régression lorsque quelqu'un a ajouté du code dans un constructeur avant qu'un certain membre ne soit initialisé.

0
user928143

Si vous écrivez une liste d'initialisation, vous faites tout en une seule étape; si vous n'écrivez pas de liste d'initialisation, vous effectuerez 2 étapes: une pour la déclaration et une pour attribuer la valeur.

0
L. Vicente Mangas

Voici un point que je n'ai pas vu d'autres y faire référence:

class temp{
public:
   temp(int var);
};

La classe temp n'a pas de ctor par défaut. Lorsque nous l'utilisons dans une autre classe comme suit:

class mainClass{
public:
 mainClass(){}
private:
  int a;
  temp obj;
};

le code ne sera pas compilé, car le compilateur ne sait pas comment initialiser obj, car il a juste un ctor explicite qui reçoit une valeur int, nous devons donc changer le ctor comme suit:

mainClass(int sth):obj(sth){}

Il ne s'agit donc pas seulement de const et de références!

0
hosh0425