Je préfère utiliser les listes d'initialisation des membres avec mes constructeurs ... mais j'ai depuis longtemps oublié les raisons derrière cela ...
Utilisez-vous des listes d'initialisation de membres dans vos constructeurs? Si oui, pourquoi? Sinon, pourquoi pas?
c++
oop
object-construction
paxos1977
la source
la source
Réponses:
Pour les membres de la classe POD , cela ne fait aucune différence, c'est juste une question de style. Pour les membres de classe qui sont des classes, cela évite un appel inutile à un constructeur par défaut. Considérer:
Dans ce cas, le constructeur de for
B
appellera le constructeur par défaut deA
, puis initialiseraa.x
à 3. Une meilleure façon serait queB
le constructeur deA
's appelle directement le constructeur de' dans la liste d'initialisation:Cela n'appellerait que
A
leA(int)
constructeur et non son constructeur par défaut. Dans cet exemple, la différence est négligeable, mais imaginez si vous voulez queA
le constructeur par défaut en fasse plus, comme l'allocation de mémoire ou l'ouverture de fichiers. Vous ne voudriez pas faire cela inutilement.De plus, si une classe n'a pas de constructeur par défaut, ou si vous avez une
const
variable membre, vous devez utiliser une liste d'initialisation:la source
Mis à part les raisons de performances mentionnées ci-dessus, si votre classe stocke des références aux objets passés en tant que paramètres de constructeur ou si votre classe a des variables const, vous n'avez d'autre choix que d'utiliser des listes d'initialisation.
la source
Une raison importante pour utiliser la liste d'initialisation du constructeur qui n'est pas mentionnée dans les réponses ici est l'initialisation de la classe de base.
Selon l'ordre de construction, la classe de base doit être construite avant la classe enfant. Sans liste d'initialisation du constructeur, cela est possible si votre classe de base a un constructeur par défaut qui sera appelé juste avant d'entrer dans le constructeur de la classe enfant.
Mais, si votre classe de base n'a qu'un constructeur paramétré, vous devez utiliser la liste d'initialisation du constructeur pour vous assurer que votre classe de base est initialisée avant la classe enfant.
Initialisation de sous-objets qui n'ont que des constructeurs paramétrés
Efficacité
En utilisant la liste d'initialisation du constructeur, vous initialisez vos membres de données à l'état exact dont vous avez besoin dans votre code plutôt que de les initialiser d'abord à leur état par défaut, puis de changer leur état en celui dont vous avez besoin dans votre code.
Si les membres de données const non statiques de votre classe ont des constructeurs par défaut et que vous n'utilisez pas la liste d'initialisation des constructeurs, vous ne pourrez pas les initialiser à l'état prévu car ils seront initialisés à leur état par défaut.
Les membres des données de référence doivent être initialisés lorsque le compilateur entre dans le constructeur car les références ne peuvent pas être simplement déclarées et initialisées ultérieurement. Cela n'est possible qu'avec la liste d'initialisation du constructeur.
la source
À côté des problèmes de performances, il y en a un autre très important que j'appellerais la maintenabilité et l'extensibilité du code.
Si un T est POD et que vous commencez à préférer la liste d'initialisation, alors si une fois T passera à un type non-POD, vous n'aurez pas besoin de changer quoi que ce soit autour de l'initialisation pour éviter les appels de constructeur inutiles car il est déjà optimisé.
Si le type T a un constructeur par défaut et un ou plusieurs constructeurs définis par l'utilisateur et une fois que vous décidez de supprimer ou de masquer le constructeur par défaut, alors si la liste d'initialisation a été utilisée, vous n'avez pas besoin de mettre à jour le code si vos constructeurs définis par l'utilisateur parce que ils sont déjà correctement mis en œuvre.
Même chose avec les membres const ou les membres de référence, disons qu'au départ T est défini comme suit:
Ensuite, vous décidez de qualifier un as const, si vous utilisiez la liste d'initialisation depuis le début, il s'agissait d'un changement de ligne unique, mais ayant le T défini comme ci-dessus, il faut également creuser la définition du constructeur pour supprimer l'affectation:
Ce n'est pas un secret que la maintenance est beaucoup plus facile et moins sujette aux erreurs si le code a été écrit non pas par un "singe de code" mais par un ingénieur qui prend des décisions basées sur une réflexion plus approfondie sur ce qu'il fait.
la source
Avant l'exécution du corps du constructeur, tous les constructeurs de sa classe parente puis de ses champs sont appelés. Par défaut, les constructeurs sans argument sont appelés. Les listes d'initialisation vous permettent de choisir quel constructeur est appelé et quels arguments ce constructeur reçoit.
Si vous avez une référence ou un champ const, ou si l'une des classes utilisées n'a pas de constructeur par défaut, vous devez utiliser une liste d'initialisation.
la source
Ici, le compilateur suit les étapes suivantes pour créer un objet de type MyClass
1. Le constructeur de type est appelé en premier pour «a».
2. L'opérateur d'affectation de "Type" est appelé à l'intérieur du corps du constructeur MyClass () pour affecter
Et puis finalement le destructeur de «Type» est appelé «a» car il sort du cadre.
Considérons maintenant le même code avec le constructeur MyClass () avec la liste d'initialisation
Avec la liste d'initialisation, les étapes suivantes sont suivies par le compilateur:
la source
Juste pour ajouter quelques informations supplémentaires pour démontrer la différence que la liste d'initialisation de membre peut faire . Dans le Leetcode 303 Range Sum Query - Immutable, https://leetcode.com/problems/range-sum-query-immutable/ , où vous devez construire et initialiser à zéro un vecteur avec une certaine taille. Voici deux implémentations et comparaisons de vitesse différentes.
Sans liste d' initialisation des membres , pour obtenir le CA, cela m'a coûté environ 212 ms .
Maintenant, en utilisant la liste d'initialisation des membres , le temps pour obtenir AC est d'environ 108 ms . Avec cet exemple simple, il est bien évident que la liste d'initialisation des membres est beaucoup plus efficace . Toutes les mesures proviennent du temps de fonctionnement de LC.
la source
Syntaxe:
Liste des besoins d'initialisation:
dans le programme ci-dessus, lorsque le constructeur de la classe est exécuté, Sam_x et Sam_y sont créés. Ensuite, dans le corps du constructeur, ces variables de données de membre sont définies.
Cas d'utilisation:
En C, les variables doivent être définies lors de la création. de la même manière en C ++, nous devons initialiser la variable Const et Reference lors de la création d'objet en utilisant la liste Initialisation. si nous faisons l'initialisation après la création de l'objet (à l'intérieur du corps du constructeur), nous obtiendrons une erreur de temps de compilation.
Objets membres de la classe Sample1 (base) qui n'ont pas de constructeur par défaut
Lors de la création d'un objet pour une classe dérivée qui appelle en interne le constructeur de classe dérivée et appelle le constructeur de classe de base (par défaut). si la classe de base n'a pas de constructeur par défaut, l'utilisateur obtiendra une erreur de temps de compilation. Pour éviter, il faut avoir soit
Le nom du paramètre du constructeur de classe et le membre de données d'une classe sont identiques:
Comme nous le savons tous, variable locale ayant la plus haute priorité puis variable globale si les deux variables ont le même nom. Dans ce cas, le programme considère la valeur «i» {variable à la fois à gauche et à droite. ie: i = i} en tant que variable locale dans le constructeur Sample3 () et la variable membre de classe (i) a été remplacée. Pour éviter, il faut utiliser soit
la source
Comme expliqué dans les directives C ++ Core C.49: préférez l'initialisation à l'affectation dans les constructeurs, cela évite les appels inutiles aux constructeurs par défaut.
la source