1
votes

(Re) Initialisation d'un objet sans constructeur de copie dans cpp

Cette question s'adresse aux systèmes embarqués!

J'ai les options suivantes pour initialiser un objet:

o.init(arg)

Cela place l'objet dans le tas et renvoie un pointeur à lui. Je préfère ne pas utiliser d'allocation dynamique dans les logiciels embarqués.

o.Object(arg)

Cela crée un nouvel objet dans la pile, puis le copie dans l'objet o et le supprime après. Cela peut provoquer un débordement de pile si l'objet est suffisamment grand pour tenir dans la RAM mais pas dans la pile. J'ai vu cela se produire avec l'optimisation désactivée, je n'ai pas exploré si c'est différent avec l'optimisation activée.

Object o(arg);

Cela crée l'objet sans faire de copie, mais comment puis-je "initialiser" o à nouveau?

Pour embarqué, je préfère la dernière option car le tas n'est pas utilisé et il n'y a pas d'objet temporaire dans la pile qui puisse faire un débordement de pile. p>

Cependant, je suis perplexe sur la façon dont je peux réinitialiser à nouveau l'objet car:

Object o = Object(arg);

n'est pas autorisé.

Je pourrais créer une méthode appelé init (arg) et appeler

Object* o = new Object(arg);

qui fait la même initialisation que Object :: Object (arg) mais je préférerais ne pas avoir à le faire créer une méthode supplémentaire avec un nom "non standard" dont je dois me souvenir.

Existe-t-il un moyen d'appeler le constructeur d'un objet déjà créé pour le réinitialiser à nouveau sans faire de copie?


5 commentaires

"Cela crée un nouvel objet dans la pile, puis le copie dans l'objet o et le supprime après." - Seulement si votre compilateur est bel et bien complètement idiot .... Ah, je vous vois compilé sans optimisations ...


Votre classe prend-elle en charge la sémantique move ou a-t-elle une fonction swap (...) ?


Si vous définissez l'objet comme Object o; alors il suffit de l'assigner à lui pour le «initialiser» à nouveau? Comme dans o = Object (); .


Par curiosité, pourquoi avez-vous besoin de «l'initialiser» à nouveau?


@StoryTeller Depuis que je programme pour embarqué, je préfère avoir le contrôle sur la mémoire et je place toutes les variables de "gros" choses en dehors de toute fonction afin qu'elles soient allouées au moment de la compilation et je sais qu'elles correspondent. J'aime les initialiser dans une fonction init car le code est un peu plus ordonné à mon avis. Dans certains cas également, il peut être nécessaire de réinitialiser un objet si je mets le processeur en veille et que certains matériels nécessitent une réinitialisation ainsi que le logiciel.


3 Réponses :


2
votes

Vous devez utiliser le placement new et appeler l'exemple de destructeur d'objet ci-dessous

A a = A();//init
a.~A();//clear
new (&a) A();//re init


6 commentaires

Ce code est IN, vous ne pouvez le supprimer explicitement que si vous avez utilisé le placement nouveau en premier lieu.


@john Non, ce n'est pas le cas, où avez-vous déjà vu ça?


Notez que cette approche peut nécessiter d'intercepter les exceptions si elles le peuvent par le constructeur de A . Sinon, le destructeur serait appelé pour un objet qui n'a pas été construit, qui est UB.


@DanielLangr je vais supposer pour cet exemple que A () construira la classe avec succès


@DanielLangr - Étant donné que l'OP met l'accent sur «intégré» à plusieurs reprises, je doute que des exceptions soient probables.


@john Je ne vois aucune exigence de ce type: eel.is/ c ++ draft / class.dtor # 12.sentence-6 , au moins dans la section sur les destructeurs.



2
votes

Pensez à utiliser un nouveau placement dans la mémoire actuelle de votre objet. Cela initialisera un nouvel objet dans la mémoire actuellement utilisée sans allouer de nouvelle mémoire. Cela pourrait être une solution pour vous. Par exemple:

#include <iostream>

class A
{
public:

    A(int i)
    {
        m_i = i;
    }

    int m_i;
};

int main()
{
    int s = 10;
    int *ps = new (&s) int(100);
    std::cout << *ps;
    std::cout << s;

    A a(5);
    new (&a) A(49);
    std::cout << a.m_i;
}

Essayez ceci:

new (&o) Object(args)

g ++ -std = c ++ 98 main.cpp renvoie 10010049, ce qui est correct.


2 commentaires

erreur: # 384: aucune instance de "l'opérateur nouveau" surchargé ne correspond à la liste d'arguments Dans quelle version de C ++ a-t-il été ajouté?


Vous trouverez ici quelques informations sur les nouveaux emplacements: geeksforgeeks.org/placement-new-operator -cpp C'est assez ancien. Je pense que C ++ - 98 devrait déjà l 'avoir. Question de débordement de pile sur le placement nouveau: stackoverflow.com/questions/222557/...



2
votes

Étape 1. Obtenez un compilateur qui fonctionne (qui reconnaît les opportunités les plus évidentes d'initialiser un objet sur place) et supporte au moins C ++ 11.

Étape 2. Pour l'initialisation, utilisez:

o.~Object(); // cleanup
new (&o) Object();

Si vous devez réaffecter avec de nouveaux arguments au constructeur:

o = std::move(o1); // o1 is the other Object, afterwards o1 is left in 'valid but undefinded state'

Si vous devez remplacer la valeur par la valeur d'un objet différent (que vous ne prévoyez pas d'utiliser par la suite:

o = Object(arg1); // calls move asignment operator

Si cela ne fonctionne pas pour vous (toujours stack thrashing / no move assignment operator, etc.), vous devriez probablement opter pour le placement new, en appelant explicitement le destructeur comme le suggère @tomer zeitune:

auto o = Object(arg); // won't copy ever with optimization turned on

L'appel explicite au destructeur est requis pour libérer par exemple toutes les ressources gérées par l'objet.


2 commentaires

Nous ne savons pas si un "objet" arbitraire fourni sans définition a des opérateurs d'affectation de déplacement ou non.


Le compilateur place toujours l'objet dans la pile pour le déplacer plus tard lorsque j'active les optimisations au niveau O3. Malheureusement, je ne semble pas pouvoir utiliser o = Object (arg1);