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

C++ Discussion :

std::vector<>.resize et exception


Sujet :

C++

  1. #1
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut std::vector<>.resize et exception
    Je me demande ce qu'il se passe lorsqu'une exception survient lors d'un vector<>.resize() par exemple.
    Supposons que le vector soit vide au départ et qu'on fasse un resize(10). L'espace est alloué en mémoire pour 10 éléments en une fois, puis chaque élément est construit un à un. Si une exception survient dans le constructeur lors de l'instantiation du 5ème élément par exemple, le vector se trouve dans un état "instable" avec seulement la moitié de ses éléments construits. En réalité l'état du vector restera sera le même que celui avant le resize() avec comme résultat un memory leak.

    En regardant différentes implémentations de vector, je ne vois pas rien empêchant ce memory leak. Ou alors quelque chose m'échapperait-il ?

  2. #2
    Membre averti
    Profil pro
    professeur des universités à la retraite
    Inscrit en
    Août 2008
    Messages
    364
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : professeur des universités à la retraite

    Informations forums :
    Inscription : Août 2008
    Messages : 364
    Points : 439
    Points
    439
    Par défaut
    Dans The C++ Standard Library de Josuttis, section 6.10.10, on lit ceci :
    For vectors, deques, and lists, you also have guarantees for resize(). It is defined as having the effect of either calling erase() or calling insert() or doing nothing.
    Pour le cas que tu évoques il semble s'agir du versant insert() et p. 240 du même ouvrage on lit ceci pour insert() :
    Either succeeds or has no effect
    Il faut noter que c'est un seul appel d'insert() qui crée l'ensemble de tes 10 éléments ou bien 'has no effect'.
    La situation que tu évoques (on construit cinq éléments sur dix et on tombe en panne) ne semblerait donc pas être possible.

  3. #3
    Membre confirmé
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Points : 588
    Points
    588
    Par défaut
    Je confirme ce que dit ptyxs.

    Voici un code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    #include <iostream>
    #include <vector>
     
    class Object
    {
    public:
    	static int i;
     
    	Object()
    	{
    		std::cout << "create : ";
    		init();
    	}
     
    	Object( const Object& o ) {
    		std::cout << "copy : ";
    		init();
    	}
     
    	void init() {
    		std::cout << i << std::endl;
    		++i;
    		if( i > 5 )
    			throw std::exception();
    	}
    };
     
    int Object::i;
     
    int main()
    {
    	Object::i = 0;
    	std::vector< Object > vec;
    	try {
    		for( int i = 0; i < 10; ++i ) {
    			vec.push_back( Object() );
    		}
    	} catch( std::exception& e ) {
    		std::cout << "exception on vec.push_back" << std::endl;
    	}
     
    	std::cout << "vec.size() = " << vec.size() << std::endl;
     
    	Object::i = 0;
    	std::vector< Object > vec2;
    	try {
    		vec2.resize(10);
    	} catch( std::exception& e ) {
    		std::cout << "exception on vec2.resize(10)" << std::endl;
    	}
     
    	std::cout << "vec2.size() = " << vec2.size() << std::endl;
     
     
    	return 0;
    }
    Et voici ce que ça donne sur GCC 4.3.3 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    create : 0
    copy : 1
    create : 2
    copy : 3
    copy : 4
    create : 5
    exception on vec.push_back
    vec.size() = 2
    create : 0
    copy : 1
    copy : 2
    copy : 3
    copy : 4
    copy : 5
    exception on vec2.resize(10)
    vec2.size() = 0

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Pour info : Exceptionally Moving!, la partie Strong Guarantee, Strong Requirements parle du reserve mais on pourrait extrapoler le principe sur le resize.

  5. #5
    Membre averti
    Profil pro
    professeur des universités à la retraite
    Inscrit en
    Août 2008
    Messages
    364
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : professeur des universités à la retraite

    Informations forums :
    Inscription : Août 2008
    Messages : 364
    Points : 439
    Points
    439
    Par défaut
    En somme : la STL c'est très bien foutu, pour finir !

  6. #6
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    J'ai un peu changé le programme en ajoutant un destructeur afin de voir si tous les objets sont bien détruits:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    #include <iostream>
    #include <vector>
     
    class Object
    {
    public:
    	static int i;
     
    	Object()
    	{
    		std::cout << "create : ";
    		init();
    	}
     
    	Object( const Object& o ) {
    		std::cout << "copy : ";
    		init();
    	}
     
    	Object & operator=(const Object &co)
    	{
    		std::cout << "assign : " << i << std::endl;
    		return *this;
    	}
     
    	~Object()
    	{
    		--i;
    		std::cout << "destruct : " << i << std::endl;
    	}
     
    	void init() {
    		std::cout << i << std::endl;
    		++i;
    		if( i > 5 )
    			throw std::exception();
    	}
    };
     
    int Object::i=0;
     
    int main()
    {
    	{
    	Object o;
    	}
     
    	std::vector< Object > vec;
    	try {
    		for( int i = 0; i < 10; ++i ) {
    			vec.push_back( Object() );
    		}
    	} catch( std::exception& e ) {
    		std::cout << "exception on vec.push_back" << std::endl;
    	}
     
    	std::cout << "vec.size() = " << vec.size() << std::endl;
     
    	std::vector< Object > vec2;
    	try {
    		vec2.resize(10);
    	} catch( std::exception& e ) {
    		std::cout << "exception on vec2.resize(10)" << std::endl;
    	}
     
    	std::cout << "vec2.size() = " << vec2.size() << std::endl;
     
     
    	return 0;
    }
    Et le résultat (sous VS2005):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    create : 0
    destruct : 0
    create : 0
    copy : 1
    copy : 2
    destruct : 2
    destruct : 1
    create : 1
    copy : 2
    copy : 3
    copy : 4
    destruct : 4
    destruct : 3
    destruct : 2
    create : 2
    copy : 3
    copy : 4
    copy : 5
    destruct : 5
    destruct : 4
    destruct : 3
    exception on vec.push_back
    vec.size() = 2
    create : 3
    copy : 4
    copy : 5
    destruct : 5
    destruct : 4
    exception on vec2.resize(10)
    vec2.size() = 0
    destruct : 3
    destruct : 2
    Ne devrait-on point voir un "destruct : 0" et même un "destruct : 1" vers la fin ?
    Je compte 15 Object construits mais seulement 13 de détruits.
    Bug VS2005, bug STL, ou c'est moi qui comprend rien ?

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    La confusion vient de la décrémentation de ta variable de classe i dans le destructeur. Tu ne sais plus qui fait quoi alors.
    Essaies ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    class Object
    {
    private:
    	static int si_nbr_instance;
    	const int instance_courante;
    public:
     
    	Object()
    	:instance_courante(++si_nbr_instance)
    	{
    		std::cout << "create : ";
    		init();
    	}
     
    	Object( const Object& o )
    	:instance_courante(++si_nbr_instance)
    	{
    		std::cout << "copy : ";
    		init();
     
    	}
     
    	Object & operator=(const Object &co)
    	{
    		std::cout << "assign : " << co.instance_courante <<" dans "<<instance_courante<< std::endl;
    		return *this;
    	}
     
    	~Object()
    	{
    		std::cout << "destruct : " << instance_courante << std::endl;
    	}
     
    	void init() {
    	   std::cout<<instance_courante;
    		if( si_nbr_instance&&((si_nbr_instance % 5 )==0)){
    		   std::cout<< " THROW" <<std::endl;
    			throw std::exception();
    		}
    		std::cout<< " OK" <<std::endl;
    	}
    };
    int Object::si_nbr_instance = 0;

  8. #8
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Le problème est le même, ça ne change rien. Ce n'est pas vector qui n'est pas robuste, c'est l'objet Object qui ne l'est pas
    Il faut d'abord appeler Init() avant d'afficher la valeur de i dans les constructeurs. En fait on affiche le petit message de "create" ou "copy" alors que l'objet n'est pas créé, ce qui prête à confusion.
    De plus, si on considère que ++i "construit" la variable statique i et que --i la "détruit", il faut la détruire juste avant le throw (on aurait pu avoir "i=new SomeObject" au lieu de ++i, ne pas mettre "delete i" avant le throw eut été une faute).
    C'est l'erreur classique de l'exception qui survient dans le constructeur alors que certains membres sont déjà initialisés (erreur qui n'est pas toujours simple à résoudre).
    Revoici donc l'objet de Julien, en mieux
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    class Object
    {
    public:
    	static int i;
     
    	Object()
    	{
    		init();
    		std::cout << "create : " << i << std::endl;
    	}
     
    	Object( const Object& o )
    	{
    		init();
    		std::cout << "copy : " << i << std::endl;
    	}
     
    	Object & operator=(const Object &co)
    	{
    		std::cout << "assign : " << i << std::endl;
    		return *this;
    	}
     
    	~Object()
    	{
    		std::cout << "destruct : " << i << std::endl;
    		--i;
    	}
     
    	void init() {
    		++i;
    		if( i > 5 )
    		{
    			std::cout << "\tTHROW : " << i << std::endl;
    			--i;
    			throw std::exception();
    		}
    	}
    };
     
    int Object::i=0;
    Conclusion, c'est OK sous VS2005.
    Par contre, c'est la cata sous MSVC6 (que j'utilise toujours ).

    J'en viens maintenant à me poser la même question à propos de boost::shared_arrray<> (que je n'ai pas encore utilisé). Je suppose qu'il est assez robuste lui aussi dans le même cas de figure, à savoir si une exception survient en cours de construction du Xème élément sur N (X<N).

  9. #9
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Sauf qu'il n'y a toujours pas de variable associée à l'objet lui-même.
    Normalement, il faudrait ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    class Object
    {
    public:
    	static int i;
     
    	/*const*/ int j; // Identifiant unique et constant de l'objet
    	int k; // Valeur réassignable de l'objet
     
    	Object()
    	{
    		init();
    		k = j = i;
    		std::cout << "create : " << j << " with value : " << k << std::endl;
    	}
     
    	Object( const Object& o )
    	{
    		init();
    		j = i;
    		k = o.k;
    		std::cout << "copy : " << j << " with value : " << k << std::endl;
    	}
     
    	Object & operator=(const Object &co)
    	{
    		// j reste inchangé, c'est volontaire
    		k = co.k;
    		std::cout << "assign : " << j << " with value : " << k << std::endl;
    		return *this;
    	}
     
    	~Object()
    	{
    		std::cout << "destruct : " << j << " with value : " << k << std::endl;
    	}
     
    	static void init() {
    		++i;
    		if( i > 5 )
    		{
    			std::cout << "\tTHROW : " << i << std::endl;
    			--i;
    			throw std::exception();
    		}
    	}
    };
     
    int Object::i=0;
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  10. #10
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Je cloture le débat puisque j'ai la réponse
    Sous MSVC6 il y a memory leak potentiel, sous VS2005 c'est OK (et sans doute aussi avec tous les compilos récents).
    Merci à tous

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

Discussions similaires

  1. Pointeur sur std::vector casser après un resize()
    Par jerem3000 dans le forum Langage
    Réponses: 3
    Dernier message: 20/01/2012, 00h30
  2. delete []double et std::vector::resize()
    Par nsarras dans le forum C++
    Réponses: 4
    Dernier message: 03/05/2011, 10h03
  3. char[50] et std::vector<>
    Par tut dans le forum SL & STL
    Réponses: 9
    Dernier message: 12/10/2004, 13h26
  4. Réponses: 8
    Dernier message: 26/08/2004, 18h59
  5. Sauvegarde std::vector dans un .ini
    Par mick74 dans le forum MFC
    Réponses: 2
    Dernier message: 12/05/2004, 13h30

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