2010-11-03 26 views
1

Je voudrais implémenter une copie de std::stack< boost::shared_ptr<T> >. Y at-il un moyen de le faire sans 3 copies? Voici le code:Copie en profondeur de std :: stack <boost> shared_ptr <T>>

template<typename T> 
void copyStackContent(std::stack< boost::shared_ptr<T> > & dst, 
         std::stack< boost::shared_ptr<T> > const & src){ 

    //// Copy stack to temporary stack so we can unroll it 
    std::stack< boost::shared_ptr<T> > tempStack(src); 

    /// Copy stack to array 
    std::vector< boost::shared_ptr<T> > tempArray; 
    while(!tempStack.empty()){ 
     tempArray.push_back(tempStack.top()); 
     tempStack.pop(); 
    } 

    /// Clear destination stack 
    while(!dst.empty()){ 
     dst.pop(); 
    } 

    /// Create destination stack 
    for(std::vector< boost::shared_ptr<T> >::reverse_iterator it = 
     tempArray.rbegin(); it != tempArray.rend(); ++it){ 
     dst.push(boost::shared_ptr<T>(new T(**it))); 
    } 
} 

et un test de l'échantillon:

void test(){ 
    // filling stack source 
    std::stack< boost::shared_ptr<int> > intStack1; 
    intStack1.push(boost::shared_ptr<int>(new int(0))); 
    intStack1.push(boost::shared_ptr<int>(new int(1))); 
    intStack1.push(boost::shared_ptr<int>(new int(2))); 
    intStack1.push(boost::shared_ptr<int>(new int(3))); 
    intStack1.push(boost::shared_ptr<int>(new int(4))); 

    // filling stack dest 
    std::stack< boost::shared_ptr<int> > intStack2; 
    copyStackContent(intStack2, intStack1); 

    assert(intStack1.size() == intStack2.size());   // same size 
    while(!intStack1.empty()){ 
     assert(intStack1.top() != intStack2.top());  // != pointers 
     assert((*intStack1.top()) == (*intStack2.top())); // same content 
     intStack1.pop(); 
     intStack2.pop(); 
    } 
} 

Répondre

2

Si vous voulez maintenir la commande, vous êtes bloqué car la pile ne fournit pas d'itérateurs. Si vous ne souhaitez pas utiliser un deque, vous pouvez au moins rendre le code plus clair (et plus efficace under certain circumstances) en passant la pile source en valeur:

template<typename T> 
void copyStackContent(std::stack< boost::shared_ptr<T> > & dst, 
         std::stack< boost::shared_ptr<T> > src){ 

    // Copy stack to array 
    std::vector< boost::shared_ptr<T> > tempArray; 
    while(!tempStack.empty()){ 
     tempArray.push_back(tempStack.top()); 
     tempStack.pop(); 
    } 

    // Clear destination stack 
    while(!dst.empty()){ 
     dst.pop(); 
    } 

    // Create destination stack 
    for(std::vector< boost::shared_ptr<T> >::reverse_iterator it = 
     tempArray.rbegin(); it != tempArray.rend(); ++it){ 
     dst.push(boost::shared_ptr<T>(new T(**it))); 
    } 
} 

Bien, je me méfie de la copie des valeurs a souligné à par shared_ptrs. Pourquoi même allouer dynamiquement si vous allez tout copier de toute façon?

+1

La pile de destination devrait probablement être retournée par valeur au lieu d'utiliser une cible. +1 –

+0

Eh bien, mon T doit être aligné lorsqu'il est alloué, donc il n'utilise pas vraiment un nouveau, mais plutôt un allocateur spécifique. – tibur

+0

Ne devrait-il pas y avoir un tempArray.reserve (src.size()) avant la première copie? Ou serait-ce une optimisation prématurée? – Basilevs

1

Non, ce que vous avez est à peu près aussi efficace que vous allez obtenir. Cependant, si vous vous trouvez en train de faire cela, vous devriez probablement utiliser un std::vector ou std::deque au lieu d'une pile. std::stack est simplement un wrapper autour d'un de ces conteneurs (généralement std::deque) Si vous utilisez l'un de ces conteneurs, vous pouvez inverser efficacement la séquence en utilisant des itérateurs inverses, et si vous utilisez un std::deque, vous pouvez même insérer de l'autre côté efficacement en utilisant push_front. Remarque: Vous devriez également avoir copyStackContent pour renvoyer une nouvelle pile au lieu de prendre une pile de destination par référence. C'est plus lisible, et il peut être moins coûteux d'allouer une nouvelle pile et simplement de désallouer l'ancienne plutôt que d'effacer tous les éléments de la pile existante.

-1

Ma première réponse était débile, n'a pas lu toute la question, voici une mise en œuvre propre d'une opération de clonage pour pile rassemblant les idées discutées ci-dessus, mais en utilisant uniquement des piles ...

template <typename T, typename _CT = boost::shared_ptr<T>, typename _ST = std::stack<_CT> > 
struct cloner 
{ 
    inline _CT copy(_CT t) 
    { 
    return _CT(new T(*t)); 
    } 

    _ST operator()(_ST src) 
    { 
    _ST temp; 

    while(!src.empty()) 
    { 
     temp.push(copy(src.top())); 
     src.pop(); 
    } 

    while(!temp.empty()) 
    { 
     src.push(temp.top()); 
     temp.pop(); 
    } 
    return src; 
    } 
}; 
+0

Cela ne copie pas le contenu de la pile, seulement la pile elle-même. –

2

Dans ce cas, le mieux est probablement d'utiliser un deque au lieu d'un stack et de changer le top en back etc au besoin. Ensuite, vous pouvez itérer et faire la copie profonde en une seule passe.

Trouvez également pourquoi vous avez besoin de la copie profonde et essayez de supprimer ce besoin à la source.

+0

+1 parce que j'ai oublié de mentionner d'essayer d'éviter la copie profonde en premier lieu. –