Question

J'ajoute souvent une méthode Vide à mes objets C ++ pour effacer l'état interne à l'aide d'un code similaire au suivant.

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        *this = Foo();
    }
};

Cela semble être mieux que de dupliquer le code dans le constructeur, mais je me suis demandé si * this = Foo () est une approche courante pour vider un objet? Y at-il des problèmes avec cette attente de me mordre à l'arrière? Y a-t-il d'autres meilleurs moyens de réaliser ce genre de chose?

Était-ce utile?

La solution

Je laisserais le constructeur appeler ma fonction à la place:

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo()
    {
        Reset();
    }

    void Reset()
    {
        n_ = 1234;
        str_ = "Hello, world!";
    }
};

Oui, vous initialisez inutilement la chaîne en tant que chaîne vide, puis vous effectuez une affectation, mais cela est beaucoup plus clair.

Autres conseils

Problèmes potentiels? Comment savez-vous que * ceci est vraiment un Foo?

Ce que vous faites avec cette méthode vide est essentiellement identique à l'affectation manuelle d'un objet nouvellement construit à une variable (ce que fait la fonction vide).

Personnellement, je supprimerais la méthode Empty et remplacerais toutes les utilisations de cette méthode par ceci:

// let's say, that you have variables foo and pfoo - they are properly initialized.
Foo foo, *pfoo;

// replace line "foo.Empty()" with:
foo = Foo();

// replace line "pfoo->Empty()" with:
delete pfoo;
pfoo = new Foo();
// or
*pfoo = Foo();

Je ne vois vraiment aucun avantage à avoir cette méthode vide. Il cache ce qui arrive réellement à l'objet sur lequel il s'appelle, le nom n'est pas non plus le meilleur choix.

Si l'appelant veut vraiment un objet propre, il n'aura aucun problème à construire lui-même l'objet.

En outre, envisagez de rendre les objets immuables, c.-à-d. , une fois construits, ils ne peuvent pas être modifiés. Dans de nombreux scénarios, cela peut vous éviter des effets secondaires imprévus.

Il y a quelque chose d'encore plus commun que ce que vous avez suggéré. en utilisant swap.

En gros, vous faites quelque chose comme ça:

T().swap(*this);

étant donné que de nombreux conteneurs standard (tous les conteneurs STL?) ont une méthode d'échange de temps constante, il s'agit d'un moyen simple et agréable d'effacer un conteneur et de vous assurer que son stockage est libéré. ??

De même, c’est un bon moyen de "se rétrécir à la taille". un conteneur également, mais en utilisant le constructeur de copie au lieu du constructeur par défaut.

envisagez d'utiliser l'emplacement nouveau :

void Empty() {
    this->~Foo();
    new (this) Foo();
}

Votre code appelle operator = , ce qui peut entraîner toutes sortes d'effets secondaires.

MODIFIER en réponse aux commentaires. - Ce code est définitivement bien défini, la norme le permet explicitement. Je vais poster le paragraphe plus tard si je trouve le temps. À propos de delete - bien sûr. Ce que je voulais dire était ~ Foo () , c’était un oubli. Et oui, Rob a également raison; la destruction de l'objet est en fait nécessaire ici pour appeler le destructeur de la chaîne.

Cela peut être une source potentielle de fuite de mémoire si vous avez une mémoire allouée dynamiquement dans le constructeur.

Voici comment je le fais:

class Foo {
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        Foo f;
        swap(f);
    }

    void swap(Foo & other) {
        std::swap(n_, other.n_);
        swap(str_, other.str_);
    }
};

void swap(Foo & one, Foo & other) {
    one.swap(other);
}

Placez la fonction swap dans le même espace de noms que la classe Foo. La recherche dépendante de l'argument le trouve lorsque les utilisateurs effectuent un appel pour permuter contre deux Foo. Vous pouvez également implémenter operator = à l'aide de votre fonction d'échange.

  

Cela semble être mieux que de dupliquer le code dans le constructeur, mais je me demandais si * this = Foo () est une approche courante pour vider un objet?

Effacer un objet n'est pas chose courante: plus souvent, un objet (peut-être même un objet immuable) est instancié et contient des données réelles, ou il n'est pas instancié.

Les réinitialisations les plus courantes sont les conteneurs ... mais vous n'écririez pas vos propres classes de conteneur maintenant, vous ne le souhaitez pas.

  

Y at-il des problèmes avec cette attente de me mordre à l’arrière?

Oui:

  • S'il ne s'agit pas vraiment d'un Foo mais d'un DerivedFoo
  • Si l'opérateur d'affectation de Foo n'existe pas ou s'il est bogué (par exemple s'il n'est pas défini et que l'opérateur par défaut n'est pas bon, par exemple parce qu'un membre de données est un pointeur nu ).
  

Existe-t-il d'autres moyens plus efficaces de réaliser ce genre de chose?

Oui, peut-être qu'une fonction libre serait préférable (éviterait les deux problèmes ci-dessus):

template<class T> void reconstruct(T* p)
{
    p->~T();
    new (p) T();
}

Oui, cela n’est pas efficace en termes de performances (créer un autre objet foo au lieu de fonctionner sur place) et vous mordra si vous allouiez de la mémoire dans le constructeur avec une fuite de mémoire désagréable.

Rendre la mémoire plus sûre appelerait ceci - > delete et this = new foo () - mais ce sera LENT.

si vous voulez être ultra-rapide, créez un champ d'objet vide statique et mémorisez-le dans Réinitialiser.

si vous voulez être rapide, assignez les propriétés une par une.

si vous souhaitez conserver un style raisonnable sans duplication, appelez Reset de Ctor comme le suggère Ates Goral, mais vous perdrez la construction plus rapide avec les paramètres par défaut.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top