web-dev-qa-db-fra.com

Appel d'un constructeur pour réinitialiser l'objet

est-il possible de réinitialiser un objet d'une classe en utilisant son constructeur?

58
cpx

Sorte de. Étant donné une classe A:

A a;
...
a = A();   

la dernière instruction n'est pas l'initialisation, c'est l'affectation, mais elle fait probablement ce que vous voulez.

73
anon

Au sens propre? Oui, en utilisant placement nouveau. Mais vous devez d'abord détruire l'objet précédemment construit.

SomeClass object(1, 2, 3);
...
object.~SomeClass(); // destruct
new(&object) SomeClass(4, 5, 6); // reconstruct
...
// Final destruction will be done implicitly

La valeur de cela ne va cependant pas au-delà de la théorie. Ne le faites pas en pratique. Le tout est laid au-delà de toute description.

47
AnT

C'est possible, bien que ce soit une très mauvaise idée. La raison en est que sans appeler les destructeurs sur l'objet existant, vous allez perdre des ressources.

Avec cette mise en garde majeure, si vous insistez pour le faire, vous pouvez utiliser le placement nouveau.

// Construct the class
CLASS cl(args);

// And reconstruct it...
new (&cl) CLASS(args);
28
R Samuel Klatchko

Non, les constructeurs ne sont appelés que lors de la première création de l'objet. Écrivez une nouvelle méthode pour le faire à la place.

Modifier

Je n'accepterai pas de nouveau placement, car je ne veux pas avoir à acheter un rapace pour le travail.

Voir cette bande dessinée , mais pensez au sujet sous la main ...

11
Dan McGrath

Réponse courte:

Non. Si une partie du comportement prévu de votre objet doit être initialisée plusieurs fois, la meilleure façon de l'implémenter est d'utiliser une méthode d'initialisation accessible. Le constructeur de votre classe peut simplement s'en remettre à cette méthode.

class C1 {
public:
  C1(int p1, int p2) {
    Init(p1,p2);
  }
  void Init(int p1, int p2) { ... }
};

Coin Nitpicker:

Existe-t-il un moyen incroyablement diabolique d'appeler un constructeur en C++ après la création d'un objet? Il s'agit presque certainement de C++ après tout. Mais c'est fondamentalement mauvais et son comportement n'est certainement pas défini par la norme et doit être évité.

10
JaredPar

En C++ 11, vous pouvez faire ceci:

#include <type_traits>

template <class T, typename... Args>
void Reconstruct(T& x, Args&&... args)
{
    static_assert(!std::has_virtual_destructor<T>::value, "Unsafe"); 
    x.~T();
    new (&x) T(std::forward<Args>(args)...);
}

Cela vous permet d'utiliser Reconstruct en passant des paramètres de constructeur arbitraires à n'importe quel objet. Cela peut éviter d'avoir à gérer un tas de méthodes Clear et des bogues qui peuvent facilement passer inaperçus si à un moment donné l'objet change, et la méthode Clear ne correspond plus au constructeur.

Ce qui précède fonctionnera bien dans la plupart des contextes, mais échouera horriblement si la référence est à une base dans un objet dérivé qui a un destructeur virtuel. Pour cette raison, l'implémentation ci-dessus empêche l'utilisation avec des objets dotés d'un destructeur virtuel.

9
denis bider

Oui, vous pouvez tricher et utiliser le placement nouveau.
Remarque: Je ne conseille pas ceci:

#include <new>

reInitAnA(A& value)
{
    value.~A();            // destroy the old one first.
    new (&value) A();      // Call the constructor 
                           // uses placement new to construct the new object
                           // in the old values location.
}
7
Martin York

J'écris généralement ce qui suit en C++ moderne:

SomeClass a;
...
a = decltype(a)();

Ce n'est peut-être pas le moyen le plus efficace, car il construit efficacement un autre objet du même type de a et l'assigne à a, mais cela fonctionne dans la plupart des cas, vous n'avez pas à rappelez-vous le type de a, et il s'adapte si le type change.

4
Coyl

Au lieu de détruire et de réinitialiser comme suggéré par certaines des réponses ci-dessus, il est préférable de faire une tâche comme ci-dessous. Le code ci-dessous est sûr d'exception.

    T& reinitialize(int x, int y)
    {
        T other(x, y);
        Swap(other); // this can't throw.
        return *this;
    }
2
Jagannath

Peut-être pas ce que vous avez en tête, mais comme vous n'avez pas mentionné à quoi cela sert, je suppose qu'une réponse serait que vous le feriez en contrôlant la portée et le flux du programme.

Par exemple, vous n'écririez pas un jeu comme celui-ci:

initialize player
code for level 1
...
reinitialize player
code for level 2
...
etc

Au lieu de cela, vous vous efforceriez de:

void play_level(level_number, level_data) {
    Player player; //gets "re-initialized" at the beginning of each level using constructor
    //code for level
}

void game() {
    level_number = 1;
    while (some_condition) {
        play_level(level_number, level_data);
        ++level_number;
    }
 }

(Plan très approximatif pour transmettre l'idée, non destiné à être compilable à distance.)

2
UncleBens

Alors que la plupart des réponses réinitialisent un objet en deux étapes; tout d'abord, créer un objet initial, puis créer un autre objet et l'échanger avec le premier à l'aide de placement new, cette réponse couvre le cas où vous créez d'abord un pointeur sur un objet vide, puis que vous l'allouez et le construisez:

class c *c_instance; // Pointer to class c
c_instance = new c(arg1, ..., argn) // Allocate memory & call the proper constructor 
// Use the instance e.g. c->data
delete c_instance; // Deallocate memory & call the destructor 
0
hmofrad