68 votes

Refactoring avec C ++ 11

Compte tenu de la nouvel ensemble d'outils fournis par le c++ beaucoup de programmeurs, visant à la simplification du code, de l'expressivité, de l'efficacité, de le parcourir par le biais de leur ancien code et de faire des ajustements (certains inutile, certains avec succès) pour atteindre leurs objectifs. Tout en essayant de ne pas perdre trop de temps sur ces travaux et la rendre non intrusive et autonome changements, quelles sont les meilleures pratiques?

Permettez-moi de croix à l'évidence :

  • Utilisation automatique pour exécuter itérateur à base de boucles :

    for (std::vector<foo>::const_iterator it(lala.begin()), ite(lala.end()); it != ite;     
    ++it);
    // becomes
    for (auto it(lala.cbegin()), ite(lala.cend()); it != ite; ++it);
    
  • Utiliser la cravate pour de multiples affectations qui vient de produire de style C lignes de code ( comment attribuer plusieurs valeurs dans une structure à la fois? )

    a = 1;
    b = 2; 
    c = 3;
    d = 4; 
    e = 5;
    // becomes
    std::tie(a, b, c, d, e) = std::make_tuple(1, 2, 3, 4, 5);
    
  • Afin de rendre une classe non héritables simplement déclarer que "final" et de supprimer le code qui atteint un tel comportement http://www.parashift.com/c++-faq/final-classes.html

  • Utilisez le mot clé delete explicitement masquer les constructeurs/destructeurs au lieu de déclarer privés (par exemple, le code pour créer des tas de base des objets, non copiable objets, etc)

  • Tour trivial foncteurs créé juste pour facillitate l'exécution d'un seul STL algorithme dans les lambda fonctions (à l'exception de code réduction de l'encombrer, vous aurez la garantie inline appels)

  • Simplifier RAII habillage d'un objet en utilisant simplement un pointeur intelligent

  • Se débarrasser de bind1st, bind2nd et il suffit d'utiliser bind

  • Remplacer écrit à la main le code pour le type de traits (Is_ptr_but_dont_call_for_const_ptrs<> et de tel :) ) avec la norme de code fourni par < type_traits >

  • Cessez y compris les boost en-têtes pour les fonctionnalités maintenant mis en oeuvre dans la STL (BOOST_STATIC_ASSERT vs static_assert)

  • Fournir une sémantique de déplacement de classes (bien que ce ne serait pas admissible comme un sale/rapide/facile à changer)

  • Utilisation nullptr , si possible, au lieu de le NULL de la macro et de se débarrasser du code des contenants remplis de pointeurs avec des 0 coulé, type d'objet

    std::vector<foo*> f(23);
    for (std::size_t i(0); i < 23; ++i)
    { f[i] = static_cast<foo*>(0); }
    // becomes
    std::vector<foo*> f(23, nullptr);
    
  • Effacer les données vectorielles accès à la syntaxe

    std::vector<int> vec;
    &vec[0];    // access data as a C-style array
    vec.data(); // new way of saying the above
    
  • Remplacer throw() noexcept (à part éviter de le obsolète exception specifiation vous obtenez certains des avantages de rapidité http://channel9.msdn.com/Events/GoingNative/2013/An-Effective-Cpp11-14-Sampler @ 00.29.42)

    void some_func() noexcept; // more  optimization options
    void some_func() throw();  // fewer optimization options
    void some_func() ;         // fewer optimization options
    
  • Remplacer le code où vous poussez un temporaire dans un récipient et espère que l'optimiseur serait ellide la copie de loin, avec un "emplace" fonction lorsqu'ils sont disponibles, afin de parfaitement en avant l'argument et de construire directement un objet dans un contenant sans que temporaire.

    vecOfPoints.push_back(Point(x,y,z)); // so '03
    vecOfPoints.emplace_back(x, y, z);   // no copy or move operations performed
    

Mise à JOUR

La réponse par Shafik Yaghmour a été à juste titre décerné par la générosité pour avoir le plus grand de l'acceptation par le public.

La réponse par R Sahu a été mon acceptée, parce que la combinaison de fonctionnalités qu'il propose capture l' esprit de refactoring : rendre le code plus clair et plus propre, plus simple et élégant.

29voto

Shafik Yaghmour Points 42198

1. Remplacement de rand

L'un des gros gains en C++11 a remplacer l'utilisation de l' rand() avec toutes les options disponibles dans l' aléatoire de l'en-tête. Remplacement d' rand() dans de nombreux cas, doit être simple.

Stephan T. Lavavej probablement fait ce point le plus fort avec sa présentation rand() Considéré comme Nocif. Les exemples montrent un uniforme entier de distribution d' [0,10] l'aide rand():

#include <cstdlib>
#include <iostream>
#include <ctime>

int main() 
{
    srand(time(0)) ;

    for (int n = 0; n < 10; ++n)
    {
            std::cout << (rand() / (RAND_MAX / (10 + 1) + 1)) << ", " ;
    }
    std::cout << std::endl ;
}

et l'utilisation de std::uniform_int_distrubution:

#include <iostream>
#include <random>

int main()
{
    std::random_device rd;

    std::mt19937 e2(rd());
    std::uniform_int_distribution<> dist(0, 10);

    for (int n = 0; n < 10; ++n) {
        std::cout << dist(e2) << ", " ;
    }
    std::cout << std::endl ;
}

Avec ce qui devrait être le déplacement de std::random_shuffle à std::shuffle qui sort de l'effort de rendre caduque rand et ses Amis. Cela a été récemment traités dans la question Pourquoi std::shuffle méthodes déprécié en C++14?.

2. En utilisant std::to_string au lieu de std::ostringstream ou sprintf

C++11 fournit std::to_string qui peut être utilisé pour convertir des nombres de std::string , il serait de produire le contenu comme l'équivalent std::sprintf. Probablement, ce serait utilisé à la place de std::ostringstream ou snprintf. C'est plus de commodité, il n'y a probablement pas beaucoup de différence de performances, et nous pouvons voir à partir de l' entier Rapide de conversion de chaîne de caractères en C++ article il y a probablement beaucoup plus rapidement des solutions de rechange disponibles, si la performance est la principale préoccupation:

#include <iostream>
#include <sstream>
#include <string>

int main()
{
    std::ostringstream mystream;  
    mystream << 100 ;  
    std::string s = mystream.str();  

    std::cout << s << std::endl ;

    char buff[12] = {0};  
    sprintf(buff, "%d", 100);  
    std::string s2( buff ) ;
    std::cout << s2 << std::endl ;

    std::cout << std::to_string( 100 ) << std::endl ;
}

3. À l'aide de constexpr en place du modèle de méta-programmation

Si vous travaillez avec des littéraux il peut y avoir des cas où l'utilisation constexpr fonctions sur le modèle de la méta-programmation peut produire du code qui est plus clair et, éventuellement, compile plus rapide. L'article Veulent la vitesse? Utilisation constexpr de la méta-programmation! fournit un exemple du premier numéro de la détermination à l'aide du modèle de méta-programmation:

struct false_type 
{
  typedef false_type type;
  enum { value = 0 };
};

struct true_type 
{
  typedef true_type type;
  enum { value = 1 };
};

template<bool condition, class T, class U>
struct if_
{
  typedef U type;
};

template <class T, class U>
struct if_<true, T, U>
{
  typedef T type;
};

template<size_t N, size_t c> 
struct is_prime_impl
{ 
  typedef typename if_<(c*c > N),
                       true_type,
                       typename if_<(N % c == 0),
                                    false_type,
                                    is_prime_impl<N, c+1> >::type >::type type;
  enum { value = type::value };
};

template<size_t N> 
struct is_prime
{
  enum { value = is_prime_impl<N, 2>::type::value };
};

template <>
struct is_prime<0>
{
  enum { value = 0 };
};

template <>
struct is_prime<1>
{
  enum { value = 0 };
};

et à l'aide de constexpr fonctions:

constexpr bool is_prime_recursive(size_t number, size_t c)
{
  return (c*c > number) ? true : 
           (number % c == 0) ? false : 
              is_prime_recursive(number, c+1);
}

constexpr bool is_prime_func(size_t number)
{
  return (number <= 1) ? false : is_prime_recursive(number, 2);
}

Le constexpr version est beaucoup plus courte, plus facile à comprendre et apparemment effectue beaucoup mieux que le modèle de méta-programmation de la mise en œuvre.

4. L'aide de la classe de l'initialisation de membre à fournir des valeurs par défaut

Comme l'a récemment couvert dans la nouvelle C++11 membres de l'initialisation de la fonction à la déclaration faite initialisation des listes obsolètes? membre de la classe d'initialisation peut être utilisé pour fournir des valeurs par défaut et de simplifier les cas où une classe possède plusieurs constructeurs.

Bjarne Stroustrup fournit un bon exemple dans le C++11 FAQ, il dit:

Cela permet d'économiser un peu de taper, mais les avantages réels de venir dans les classes avec plusieurs constructeurs. Souvent, tous les constructeurs utilisent une commune de l'initialiseur d'un membre:

et fournit un exemple des membres qui ont en commun un initialiseur:

class A {
  public:
    A(): a(7), b(5), hash_algorithm("MD5"), s("Constructor run") {}
    A(int a_val) : a(a_val), b(5), hash_algorithm("MD5"), s("Constructor run") {}
    A(D d) : a(7), b(g(d)), hash_algorithm("MD5"), s("Constructor run") {}
    int a, b;
  private:
    HashingFunction hash_algorithm;  // Cryptographic hash to be applied to all A instances
    std::string s;                   // String indicating state in object lifecycle
};

et dit:

Le fait que hash_algorithm et s, chacun a un seul défaut c'est perdu dans le désordre de code et pourrait facilement devenir un problème lors de l'entretien. Au lieu de cela, nous pouvons facteur de l'initialisation des données membres:

class A {
  public:
    A(): a(7), b(5) {}
    A(int a_val) : a(a_val), b(5) {}
    A(D d) : a(7), b(g(d)) {}
    int a, b;
  private:
    HashingFunction hash_algorithm{"MD5"};  // Cryptographic hash to be applied to all A instances
    std::string s{"Constructor run"};       // String indicating state in object lifecycle
};

5. L'utilisation de largeur fixe les types d'entiers de cstdint au lieu de roulé à la main typedefs

Depuis le C++11 standard utilise du C99 normatif de référence, nous obtenons une largeur fixe les types integer, ainsi. Par exemple:

int8_t
int16_t 
int32_t 
int64_t 
intptr_t

Bien que plusieurs d'entre eux facultatif, pour la largeur exacte entier les types suivants de C99 section 7.18.1.1 s'applique:

Ces types sont en option. Toutefois, si une mise en œuvre fournit les types d'entiers avec des largeurs de 8, 16, 32 ou 64 bits, pas de rembourrage bits, et (pour les types signés) avoir une représentation en complément à deux, il doit définir les correspondant typedef noms.

18voto

R Sahu Points 24027

J'ajouterais des constructeurs délégués et des initialiseurs de membres de classe à la liste.

Simplification en utilisant des constructeurs délégués et une initialisation en classe

Avec C ++ 03:

 class A
{
  public:

    // The default constructor as well as the copy constructor need to 
    // initialize some of the members almost the same and call init() to
    // finish construction.
    A(double data) : id_(0), name_(), data_(data) {init();}
    A(A const& copy) : id_(0), name_(), data_(copy.data_) {init();}

    void init()
    {
       id_ = getNextID();
       name_ = getDefaultName();
    }

    int id_;
    string name_;
    double data_;
};
 

Avec C ++ 11:

 class A
{
  public:

    // With delegating constructor, the copy constructor can
    // reuse this constructor and avoid repetitive code.
    // In-line initialization takes care of initializing the members. 
    A(double data) : data_(data) {}

    A(A const& copy) : A(copy.data_) {}

    int id_ = getNextID();
    string name_ = getDefaultName();
    double data_;
};
 

12voto

voodooattack Points 829

Pour chaque syntaxe:

 std::vector<int> container;

for (auto const & i : container)
  std::cout << i << std::endl;
 

7voto

Mantosh Kumar Points 2759

Caractéristique: std :: move

"Exprimer une nette différence entre la copie et le déplacement des ressources"

 std::string tmp("move");
std::vector<std::string> v;
v.push_back(std::move(tmp));
//At this point tmp still be the valid object but in unspecified state as
// its resources has been moved and now stored in vector container.
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X