2017-07-25 5 views
1

Contexte: nuances de programmation et d'initialisation de liste d'initialisation, en particulier celle qui, selon moi, devrait appeler le constructeur de copie.Nuances de pointeur/référence/copie d'initialisation C++

Question: L'instanciation d'une classe dans la liste d'initialisation d'une autre classe pour initialiser une variable membre tenue en valeur appelle-t-elle le constructeur de copie? Exemple fourni ci-dessous où TestClassCopy a une variable membre TestMember maintenue par valeur par opposition au pointeur ou à la référence. Ce cppreference page ne semble pas couvrir suffisamment ceci dans les exemples fournis.

Question supplémentaire: Un constructeur de copie invoqué dans une liste d'initialisation produit-il un impact de perfusion temps/espace? On dirait qu'un compilateur devrait pouvoir l'optimiser si la spécification C++ le permet.


Voici le code (build testé avec l'ensemble des outils de VS2015):

TestMember.h (ne montrant pas TestMember.cpp pour des raisons d'espace)

#pragma once 

#include <stdint.h> 

class TestMember { 
public: 
    TestMember(uint8_t); 

private: 
    uint8_t m_value; 
}; 

TestClassCopy.h

#pragma once 

#include "test_member.h" 

class TestClassCopy { 
public: 
    TestClassCopy(); 
    virtual ~TestClassCopy(); 

private: 
    TestMember m_member; 
}; 

TestClassCopy.cpp

#include "test_class_copy.h" 

TestClassCopy::TestClassCopy() : 
    m_member(TestMember(255)) { // invokes copy constructor yes? 
} 

TestClassCopy::~TestClassCopy() { 
} 

Pour être complet, d'autres choses où je pourrais être faire des suppositions je ne devrais pas:

Pour un pointeur de membre à un TestMember, un « nouveau » dans le initialiseur liste et un 'delete' dans le destructeur devrait être suffisant. Pour une référence de membre, je crois comprendre qu'il y a encore plus de nuance dans le fait que si la référence est transmise au constructeur, vous pouvez simplement l'assigner (puisque la durée de vie est gérée en dehors de la liste d'initialisation). Toutefois,, si le membre de test est instancié dans la liste d'initialisation (dans une référence), il s'agit d'un non-non puisque le testMember temporaire disparaît une fois l'initialisation terminée.

TestMemberReference.h

#pragma once 

class TestMember; 

class TestClassReference { 
public: 
    TestClassReference(); 
    virtual ~TestClassReference(); 

private: 
    TestMember& m_member; 
}; 

TestMemberReference.cpp

#include "test_class_reference.h" 

#include "test_member.h" 

TestClassReference::TestClassReference() : 
    m_member(TestMember(255)) { // ew, don't do this; TestMember temporary will go out of scope 
} 

TestClassReference::~TestClassReference() { 
} 
+0

Dans votre premier exemple, la [copie est élue] (https://stackoverflow.com/questions/12953127/what-are-copy-elision-and -return-value-optimization), il n'y a pas de copie effectuée. – CoryKramer

+0

vous pouvez toujours mettre une déclaration d'impression ou un point de rupture dans le constructeur de la copie –

+0

@CoryKramer, parfait c'est ce que je cherchais merci. Je suppose que cela signifie que vous dites 'oui, le constructeur de la copie pourrait être invoqué mais en raison de l'élision, il peut ou non dépendre des paramètres d'optimisation du compilateur, etc.'. PS. Si vous voulez créer une réponse complète, je peux l'accepter comme une réponse. – azydevelopment

Répondre

0

Ce cppreference page ne semble pas couvrir ce suffisamment dans les exemples fournis

qui cppreference la page dit « Initialise la base ou membre nommé par classe ou identifiant à l'aide direct initialization » et si vous cliquez sur cela, vous pouvez lire que, à partir de C++ 17,

si l'initialiseur est une expression prvalue qui se type cv-non qualifié est la même classe que T, l'expression initialiseur lui-même, plutôt que temporaire matérialisée de celui-ci, est utilisé pour initialiser l'objet de destination: voir copie élision

donc la réponse est: en C + +17 aucun constructeur de copie n'est appelé. Avant C++ 17, un constructeur de copie pouvait techniquement être appelé, mais l'élision de la copie a éliminé cet appel. Vous pouvez toujours l'observer, comme preuve, en compilant votre exemple avec g++ -fno-elide-constructors -O0 -std=c++14