IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

*this == référence valide dans un destructeur ?


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 492
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 492
    Billets dans le blog
    1
    Par défaut *this == référence valide dans un destructeur ?
    Bonjour,

    J'ai une classe qui joue un rôle d'observateur et qui possède une liste d'observables (pour pouvoir être notifié par plusieurs sources). Dans le destructeur de cette classe, je parcours la liste et pour chaque observable, je demande à ce que mon objet soit retiré de la liste des observateurs. Le code est comme ceci :

    (observateur)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    MultipleDefaultObserver::~MultipleDefaultObserver() {
    	for (auto& detector : detectors_m) {
    		if (detector != nullptr) {
    			detector->removeObserver(*this);
    		}
    	}
    }
    (observable)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void DefaultDetector::removeObserver(DefaultObserver& observer) {
    	for (auto& slot : observers_m) {
    		if (slot == &observer) {
    			slot = nullptr;
    			break;
    		}
    	}
    }
    Comme je ne fais pas d'allocation dynamique, detectors_m et observers_m sont des std::array de pointeurs.

    J'ai écris ce code, donc il me parait OK, sinon j'aurais fait autrement. Valgrind n'est pas de mon avis et me signalent des invalid read/write of size 4 dans le destructeur. En commentant la ligne detector->removeObserver(*this);, les erreurs disparaissent. Un exemple de message de Valgrind :
    ==1687== Invalid read of size 4
    ==1687== at 0x12E5A9: supervisor::DefaultDetector::removeObserver(supervisor::DefaultObserver&) (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x12E917: supervisor::MultipleDefaultObserver::~MultipleDefaultObserver() (in /media/sf_shared/Common/cmake-debug/common)

    ==1687== by 0x26A6F3: ____C_A_T_C_H____T_E_S_T____8() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x181BD2: Catch::RunContext::invokeActiveTestCase() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1933D0: Catch::RunContext::runCurrentTest(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1A4FF0: Catch::RunContext::runTest(Catch::TestCase const&) (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1A58FC: Catch::(anonymous namespace)::runTests(std::shared_ptr<Catch::Config> const&) (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1AA01D: Catch::Session::runInternal() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1AA160: Catch::Session::run() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x114FD3: main (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== Address 0x4dcfb90 is 0 bytes inside a block of size 440 free'd
    ==1687== at 0x48318D7: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
    ==1687== by 0x26A6D7: ____C_A_T_C_H____T_E_S_T____8() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x181BD2: Catch::RunContext::invokeActiveTestCase() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1933D0: Catch::RunContext::runCurrentTest(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1A4FF0: Catch::RunContext::runTest(Catch::TestCase const&) (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1A58FC: Catch::(anonymous namespace)::runTests(std::shared_ptr<Catch::Config> const&) (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1AA01D: Catch::Session::runInternal() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1AA160: Catch::Session::run() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x114FD3: main (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== Block was alloc'd at
    ==1687== at 0x483087B: operator new(unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
    ==1687== by 0x26A458: ____C_A_T_C_H____T_E_S_T____8() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x181BD2: Catch::RunContext::invokeActiveTestCase() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1933D0: Catch::RunContext::runCurrentTest(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1A4FF0: Catch::RunContext::runTest(Catch::TestCase const&) (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1A58FC: Catch::(anonymous namespace)::runTests(std::shared_ptr<Catch::Config> const&) (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1AA01D: Catch::Session::runInternal() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x1AA160: Catch::Session::run() (in /media/sf_shared/Common/cmake-debug/common)
    ==1687== by 0x114FD3: main (in /media/sf_shared/Common/cmake-debug/common)
    Vu que j'utilise un auto-range for, je ne pense pas dépasser des tableaux. La seule hypothèse qui me vient à l'esprit est que *this n'est plus une référence valide à ce moment là. Vous pouvez confirmer ? Comment faire pour que ça fonctionne correctement ?

    Merci d'avance !

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 145
    Billets dans le blog
    4
    Par défaut
    Pour moi il n'y a pas de probleme dans ce code. this est encore valide dans le destructeur, *this l'est tout autant.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2007
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 37
    Par défaut
    Si MultipleDefaultObserver est une classe mère, peut-être qu'il considère qu'une fois dans le destructeur de celle-ci, une partie de *this n'est plus valide (destruction des membres dérivés) et rapporte une erreur alors que effectivement l'adresse est valide ?

  4. #4
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 492
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 492
    Billets dans le blog
    1
    Par défaut
    Effectivement, dans mon code de test, MultipleDefaultObserver est une classe mère. Les erreurs se produisent lors de la destruction d'instance de MyMultipleDefaultObserver, une implémentation faite pour les tests unitaires.

    J'ai donc essayé de créer une méthode protected nommée stop() dans laquelle j'ai extrait le boulot du destructeur. Ainsi, le destructeur de MultipleDefaultObserver est déclaré = default et le destructeur de MyMultipleDefaultObserver appelle stop().

    J'ai toujours les mêmes erreurs

    C'était une très bonne idée en tout cas

  5. #5
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 492
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 492
    Billets dans le blog
    1
    Par défaut
    C'est bon j'ai trouvé !

    Le problème n'est pas (vraiment) dans la classe mais dans son utilisation. En fait, je me suis rendu compte que seul deux de mes test cases généraient des erreurs avec Valgrind. En voici un :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    TEST_CASE("It is possible to know how many detectors are currently observed", "[supervisor][MultipleDefaultObserver]") {
    	MyMultipleDefaultObserver observer;
     
    	CHECK(observer.getDetectorCount() == 0);
     
    	auto maxCount = observer.getMaxDetectorCount();
    	std::vector<MyDefaultDetector> detectors(maxCount);
     
    	for (std::size_t i = 0; i < maxCount; ++i) {
    		observer.observe(detectors[i]);
    		CHECK(observer.getDetectorCount() == i + 1);
    	}
    }
    Le problème est que le vecteur detectors est détruit avec observer.

    Du coup j'ai créé un bloc à l'intérieur de la fonction : le vecteur est créé en dehors, l'observer à l'intérieur. Valgrind est content ; moi aussi mais pas tant que ça car je me dis que ce destructeur est à risque... Qu'en pensez-vous ?

  6. #6
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2007
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 37
    Par défaut
    Ok je comprends, j'avoue que dans ce genre de design pattern je préfère que seul un des deux (observateur ou observé) ait une liste de l'autre.
    Cela oblige à gérer "explicitement" la liste, mais cela évite une gestion croisée qui rend le code plus difficile à lire/tracer ou encore des difficultés à l'utilisation (comme dans ton cas).

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 8
    Dernier message: 20/05/2008, 14h49
  2. [liste] référence unique dans une liste
    Par jean-jacques varvenne dans le forum Général Python
    Réponses: 3
    Dernier message: 29/03/2005, 09h57
  3. [STRUTS][Validation] - Validation dans un execute
    Par SEMPERE Benjamin dans le forum Struts 1
    Réponses: 8
    Dernier message: 07/12/2004, 10h55
  4. Récupération de données validées dans une pop-up
    Par hdd dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 01/12/2004, 16h47
  5. problème de références _ptr dans une map STL
    Par Mr_Tyu dans le forum CORBA
    Réponses: 1
    Dernier message: 10/08/2004, 10h39

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo