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 :

Optimisation d'une classe vecteur


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Nouveau candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2023
    Messages
    1
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 23
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2023
    Messages : 1
    Par défaut Optimisation d'une classe vecteur
    - Bonjour, je suis étudiant et je cherche à optimiser une classe vecteur en c++.

    Pour cela, il me faut implémenter les caractéristiques suivantes :
    - Distinction entre l'Allocation/déallocation de stockage et l'initialisation/destruction
    -Small Buffer Optimization : On ajoute un buffer de capacité N. Si la taille du vecteur n'excède pas N (vecteur court), on a pas besoin d'allouer dynamiquement le stockage pour les éléments du vecteur. (La Capacité d'un vecteur construit par le constructeur par défaut est N)

    Contenu du vecteur :
    - Constructeur par défaut
    -Constructeur/Opérateur d'attribution de Copie/de Déplacement
    -Destructeur
    -Fonctions Reserve, Push Back, emplace back,pop back, clear, swap.
    -Accès au premier élément, à l'élément i, à la capacité et à la taille du vecteur

    Je suis débutant en C++/langages bas niveau et j'ai choisi un cours trop avancé qui fait que, malgré le travail, j'arrive à un point où je suis perdu. J'ai donc besoin de votre aide,
    Voici un extrait de mon code, je ne sais pas si il est correct, il comporte les constructeurs/opérateurs d'attribution par défaut et de copie.

    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
    71
    72
    73
     
    #pragma once
     
     
    #include <iostream>
    #include <memory>
     
     
     
     
    namespace epc{
     
     
    template <typename T, size_t N>
    class vector
    {
    private:
        //initialisation des variables
        T* _data;
        size_t _size; 
        size_t _capacity;
        alignas(T) unsigned char m_buffer[N * sizeof(T)]; //initialisation du buffer
     
     
    public:
      vector() noexcept : _data(nullptr), _size(0), _capacity(0) {}; //Constructeur par défaut
     
     
    // Appel du constructeur par défaut pour s'assurer que le vecteur est bien initialisé.
      explicit vector(std::size_t s) : _size(s), _capacity(s) {
        //Test si la taille est supérieur à la taille du buffer et alloue la mémoire en conséquence
        if (s > N) {
          _data = reinterpret_cast<T*>(::operator new(sizeof(T) * s));
        } else {
          _data = reinterpret_cast<T*>(&m_buffer);
        }
        //Construction par défaut
        for (std::size_t i = 0; i < _size; i++) {
            new(_data + i) T();
        }
    }
     
     
    // Copy constructor
      vector(const vector &other) : _size(other._size), _capacity(other._capacity) {
        //Test si la taille est supérieur à la taille du buffer et alloue la mémoire en conséquence
        if (other._capacity > N) {
          _data = reinterpret_cast<T*>(::operator new(sizeof(T) * other._capacity));
        } else {
          _data = reinterpret_cast<T*>(&m_buffer);
        }
        //Construction par copie
        for (std::size_t i = 0; i < _size; i++) {
           new(_data + i) T(other._data + i);  
        }
    }  
     
    //copy assignment operator
        vector &operator=(const vector &other) {
            //Copie la taille et la capacité de la variable rhs
            _size = other._size;
            _capacity = other._capacity;
            //alloue la mémoire en fonction de la taille de la variable rhs
            _data = reinterpret_cast<T*>(::operator new(sizeof(T) * _capacity));
            //Construction par copie
            for (std::size_t i = 0; i < _size; i++) {
            new(_data + i) T(other._data + i);  
            }
            return *this;
        }
     
     
    }}


    J'aimerais me concentrer sur cette partie d'abord pour être sur de comprendre comment bien implémenter ces constructeurs en gérant le buffer et différencier les cas ou le vecteur est long (taille >N) et celui ou il est court. Y'a t'il des erreurs, de syntaxe, de logique ? Qu'est ce qu'une variable rhs et à quoi elles servent dans ce code ?
    Je ne comprend pas cette ligne : _data = reinterpret_cast<T*>(::operator new(sizeof(T) * other._capacity)); Alloue t'elle bien un nouveau buffer plus grand ?

    Merci beaucoup pour votre aide.

  2. #2
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Bonjour,

    La variable rhs, c'est l'objet passé en paramètre, donc ici c'est la variable other.

    Oui, l'expression _data = reinterpret_cast<T*>(::operator new(sizeof(T) * other._capacity)); alloue un buffer pouvant contenir other._capacity objets de type T.

    Gérer une collection est complexe, le plus gros piège est la gestion des éventuelles exceptions. Les allocations, les constructions et les copies des objets peuvent déclencher des exceptions et on doit garantir que quoi qu'il arrive le vecteur reste cohérent et sans perte de mémoire. Le code les gérant est irréaliste pour un débutant et même pour une personne confirmée. C'est pour cela que dans la vraie vie, on ne se risque pas à créer une collection qui géré ses allocations. Je vais supposer qu'ici, les exceptions sont négligées.

    Le moyen le plus simple et le plus sûr pour écrire l'opérateur d'affectation, est d'écrire une fonction void swap(vector &other) (très facile) et de l'utiliser.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    vector &operator=( const vector &other ) {
        vector  temp( other );      // construction d'une copie de other
        temp.swap( *this );         // échange de *this et de temp, donc *this est maintenant la copie construite de other
        return  *this;
    }                               // ici temp est détruit, donc ce qu'était *this disparait
    On peut sinon écrire l'opérateur d'affectation étape par étape, c'est plus complexe mais nous donne la possibilité d'optimiser et évitant des allocations si possible. Les actions à effectuer sont:
    A) cas non optimisé
    -- désallouer ce qu'il y a dans *this.
    -- allouer ce qui est nécessaire (assez pour recevoir ce qu'il y a dans other.
    -- this->_capacity = taille allouée(au moins other._size).
    -- construire une copie de chacun des éléments de other.
    -- this->_size = other->_size;.
    -- attention, si this == &other, on tente de copier un objet sur lui-même, ce cas ne marchera pas.
    B) cas optimisé, par exemple si this->_capacity >= other._size, on n'a pas besoin de toucher aux allocations
    -- soit std::size_t n = std::min(this->_size,other._size);.
    -- affecter par copie les n premiers éléments (indices qui existaient dans *this et existent dans other)
    -- si n < this->_size, détruire les éléments en trop
    -- si other._size > n, construire par copie les éléments manquants.
    -- this->_size = other._sizePour gérer sainement les exceptions, il y aurait des précautions supplémentaires, et la chronologie serait différentes (cas A ou cas B).

Discussions similaires

  1. Optimiser l'intanciation d'une classe
    Par olibara dans le forum C#
    Réponses: 7
    Dernier message: 17/10/2008, 07h35
  2. Réponses: 2
    Dernier message: 08/12/2006, 01h20
  3. Mettre un vecteur template dans une class
    Par Dimitri_87 dans le forum Langage
    Réponses: 8
    Dernier message: 04/12/2006, 19h33
  4. Réponses: 6
    Dernier message: 28/09/2005, 11h30
  5. [Debutant][Collection] Création d'un vecteur de type d'une classe
    Par Tao® dans le forum Collection et Stream
    Réponses: 11
    Dernier message: 22/04/2004, 17h06

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