Table of Contents

Le constructeur de recopie

Le constructeur de recopie est un constructeur qui initialise un objet comme étant une copie conforme d'un objet déjà existant et de même type.

{
    std::string message = "Bonjour";
    std::string copie_message(message);
          // Création d'un nouvel object de type std::string
          // et appel du constructeur de recopie. L'objet final
          // contient une copie de la chaîne de caractère "Bonjour".
    std::string autre_copie = message;
    std::string encore_une_autre_copie = std::string(message);
          // Autres syntaxes d'appel du constructeur de recopie.
    std::string* copie_sur_le_tas = new std::string(message);
          // Autre syntaxe d'appel du constructeur de recopie.    
}

Dans le cas de type simple comme des valeurs numériques, des valeurs booléenes,

Déclaration du constructeur par défaut

Le constructeur de recopie est déclarée comme le constructeur prenant une référence sur un objet de même type ne pouvant être modifié.

class MyClass
{
public:
    MyClass(const MyClass&)      // Constructeur de recopie.
    {}
};

Ce constructeur doit initialiser l'objet créé comme étant une copie de l'objet initial. Supposant par exemple que l'objet initial soit un objet qui contient une chaîne de caractère au format C, ie. terminé par un zéro.

class CString
{
private:
    const char* m_string;
public:
    CString(const char* aString): 
        m_string(strdup(aString)
    {}
    ~CString()
    {
        free(m_string);
    }
};    

Cette classe duplique la chaine de caractère fournit en argument. Pour cela, la mémoire nécessaire est allouée par la fonction strdup et une copie de la chaîne est effectuée. Quand un objet de type CString est détruit, la mémoire est libérée en effectuant un appel à free.

Si nous souhaitant effectuer une duplication, nous ne pouvons pas recopier la valeur du pointeur m_string. En effet, la recopie serait imparfaite puisque les deux classes feront référence à un bloc mémoire partagée et ce bloc mémoire serait libérée au moment de la destruction de la première des deux classes, ce qui fait qu'au moment de la destruction de la seconde classe, une erreur se produira puisque la mémoire a déjà été libérée.

De ce fait, l'opération de recopie impose de dupliquer la chaîne référencée par la classe d'origine.

class CString
{
private:
    const char* m_string;
public:
    CString(const char* aString): 
        m_string(strdup(aString)
    {}
    CString(const CString& aString): 
        m_string(strdup(aString.m_string)
    {}
    ~CString()
    {
        free(m_string);
    }
};    

Ceci démontre bien que le mécanisme par défaut de recopie des valeurs des différents champs n'est pas suffisant et que dans de nombreux cas, l'opération de recopie est plus complexe et nécessite des allocations de mémoire.

La génération automatique du constructeur de recopie

Si aucun constructeur de recopie n'est défini pour une classe héritant de plusieurs classes, un constructeur de recopie est automatiquement généré en C++.

class MyClass: 
  public BaseClass, 
  private ImplementationClass
{
    private:
        int value;
    public:
        MyClass(const MyClass& anotherClass): 
            BaseClass(anotherClass), 
            ImplementationClass(anotherClass), 
            value(anotherClass.value) 
                // Correspond au constructeur de recopie généré automatiquement
                // en C++.
        {}
}; 

Comme MyClass hérite de BaseClass et de ImplementationClass, la conversion de anotherClass qui a pour type const MyClass& en une référence à un objet de type BaseClass ou Implementation est toujours possible puisque que MyClass hérite de l'ensemble des champs et fonctions membres de BaseClass et de ImplementationClass et par extension pour toujours se comporter comme étant un objet ayant pour type BaseClass ou pour type ImplementationClass.