web-dev-qa-db-fra.com

Comment passer au C ++ 11?

Je programme en C++ depuis un certain temps maintenant, mais surtout des choses centrées sur les fonctionnalités de bas niveau de C++. J'entends par là surtout travailler avec des pointeurs et des tableaux bruts. Je pense que ce comportement est connu comme utilisant C++ comme C avec des classes. Malgré cela, je n'ai essayé le C que récemment pour la première fois. J'ai été agréablement surpris de voir comment des langages comme C # et Java cachent ces détails dans des classes de bibliothèque standard pratiques comme les dictionnaires et les listes.

Je suis conscient que la bibliothèque standard C++ contient de nombreux conteneurs tels que des vecteurs, des cartes et des chaînes et C++ 11 ne fait qu'ajouter à cela en ayant std :: array et des boucles à distance.

Comment puis-je apprendre au mieux à utiliser ces fonctionnalités du langage moderne et qui conviennent à quels moments? Est-il vrai que l'ingénierie logicielle en C++ est aujourd'hui largement exempte de gestion manuelle de la mémoire?

Enfin, quel compilateur dois-je utiliser pour tirer le meilleur parti de la nouvelle norme? Visual Studio possède d'excellents outils de débogage, mais même VS2012 semble avoir une prise en charge terrible de C++ 11.

35
Overv

Tout d'abord, quelques règles d'or:

  • Utilisez std::unique_ptr Comme pointeur intelligent sans frais généraux. Vous ne devriez pas avoir à vous soucier des pointeurs bruts si souvent. std::shared_ptr Est également inutile dans la plupart des cas. Un désir de propriété partagée trahit souvent un manque de réflexion sur la propriété en premier lieu.

  • Utilisez std::array Pour les tableaux de longueur statique et std::vector Pour dynamique.

  • Utiliser largement des algorithmes génériques, notamment:

    • <algorithm>
    • <numeric>
    • <iterator>
    • <functional>
  • Utilisez auto et decltype() partout où ils sont lisibles. En particulier, lorsque vous souhaitez déclarer une chose, mais d'un type qui ne vous intéresse pas, comme un itérateur ou un type de modèle complexe, utilisez auto. Lorsque vous voulez déclarer une chose en termes de type d'une autre chose, utilisez decltype().

  • Assurez-vous que les choses sont sécuritaires lorsque vous le pouvez. Lorsque vous avez des assertions qui imposent des invariants sur un type particulier de chose, cette logique peut être centralisée dans un type. Et cela n'entraîne pas nécessairement de surcharge pour l'exécution. Il va également de soi que les transtypages de style C ((T)x) Doivent être évités au profit des transtypages de style C++ plus explicites (et consultables!) (Par exemple, static_cast).

  • Enfin, sachez comment la règle des trois:

    • Destructeur
    • Constructeur de copie
    • Opérateur d'assignation

    Est devenu la règle des cinq avec l'ajout du constructeur de déplacement et de l'opérateur d'affectation de déplacement. Et comprenez les références rvalue en général et comment éviter la copie.

C++ est un langage complexe, il est donc difficile de caractériser la meilleure façon d'utiliser tous de celui-ci. Mais les pratiques d'un bon développement C++ n'ont pas fondamentalement changé avec C++ 11. Vous devriez toujours préférer les conteneurs gérés par la mémoire à la gestion manuelle de la mémoire: les pointeurs intelligents facilitent cette opération.

Je dirais que le C++ moderne est en effet en grande partie exempt de gestion manuelle de la mémoire - l'avantage du modèle de mémoire de C++ est qu'il est déterministe, et non pas qu'il est manuel. Les désallocations prévisibles améliorent les performances.

En ce qui concerne un compilateur, G ++ et Clang sont tous deux compétitifs en termes de fonctionnalités C++ 11 et rattrapent rapidement leurs lacunes. Je n'utilise pas Visual Studio, donc je ne peux ni pour ni contre.

Enfin, une note sur std::for_each: Évitez-le en général.

transform, accumulate et eraseremove_if sont de bonnes vieilles fonctions map, fold et filter. Mais for_each Est plus général, et donc moins significatif - il n’exprime pas intention autre que le bouclage. En plus de cela, il est utilisé dans les mêmes situations que for basé sur une plage et est syntaxiquement plus lourd, même lorsqu'il est utilisé sans point. Considérer:

for (const auto i : container)
    std::cout << i << '\n';

std::for_each(container.begin(), container.end(), [](int i) {
    std::cout << i << '\n';
});

for (const auto i : container)
    frobnicate(i);

std::for_each(container.begin(), container.end(), frobnicate);
50
Jon Purdy

Comme point de départ:

  • Arrête d'utiliser char* pour les chaînes. Utilisation std::string ou std::wstring et regardez votre code devenir plus court, plus lisible et plus sûr
  • Arrêtez d'utiliser des tableaux de style C (les choses déclarées avec [ ]) et utilise std::vector ou une autre classe de conteneur appropriée. Les belles choses sur std::vector est qu'il connaît sa propre longueur, qu'il nettoie son contenu lorsqu'il sort du cadre, qu'il est facile de le parcourir et qu'il s'agrandit lorsque vous ajoutez d'autres éléments. Mais il existe d'autres collections qui pourraient fonctionner encore mieux dans votre situation.
  • Utilisation std::unique_ptr - et apprendre std::move presque immédiatement. Étant donné que cela peut entraîner certains objets non copiables, la paresse peut parfois vous envoyer vers std::shared_ptr - et vous pouvez avoir des cas d'utilisation authentiques pour std::shared_ptr ainsi que
  • Utilisez auto lors de la déclaration d'itérateurs et de types qui dépendent de déclarations antérieures (par exemple, auparavant, vous avez déclaré un vecteur de quelque chose, maintenant vous déclarez quelque chose, utilisez auto)
  • Utilisez des algorithmes et for_each sur un "raw for" chaque fois que vous le pouvez car cela évite aux autres de lire attentivement votre boucle pour conclure que vous êtes en fait en train d'itérer sur toute la collection, etc. Si votre compilateur prend en charge "range for", utilisez-le sur for_each. Apprenez des appels d'algorithme triviaux comme iota, generate, accumulate, find_if etc.
  • Utilisez lambdas - ils sont le moyen facile de tirer parti des algorithmes. Ils ouvrent également la porte à bien plus.

Ne vous préoccupez pas trop du compilateur à utiliser. Le manque "terrible, horrible" de prise en charge de C++ 11 dans VS2012 est qu'il n'y a pas de modèles variadic (ouais, vous étiez à peu près pour utiliser variadic modèles) et le {} l'initialiseur n'est pas là. Je le veux aussi, mais je ne vais pas arrêter d'utiliser un outil de développement utile dessus.

La deuxième chose à faire, après avoir embrassé std::, c'est commencer à penser RAII. Chaque fois que vous avez

  • commencer l'action
  • série d'actions avec quelque chose que vous avez obtenu en commençant l'action
  • action de nettoyage qui doit se produire même en cas d'exceptions

Ensuite, vous avez un constructeur, un certain nombre de fonctions membres et un destructeur. Écrivez un cours qui s'en occupe pour vous. Vous pourriez même ne pas avoir à écrire le ctor et le dtor. Mettre un shared_ptr en tant que variable membre d'une classe est un exemple de RAII - vous n'écrivez pas de code de gestion de la mémoire, mais lorsque votre instance sort de la portée, les bonnes choses se produisent. Développez cette réflexion pour couvrir des choses comme la fermeture de fichiers, la libération de poignées, de verrous, etc. et le code deviendra simplement plus simple et plus petit (tout en éliminant les fuites) devant vos yeux.

Si vous vous sentez vraiment en confiance, purgez printf au profit de cout, supprimez les macros (#definestuff), et commencez à apprendre certains "idiomes avancés" comme PIMPL. J'ai un cours complet à ce sujet sur Pluralsight que vous pouvez probablement regarder en utilisant leur essai gratuit.

12
Kate Gregory

Comment puis-je apprendre au mieux à utiliser ces fonctionnalités du langage moderne et qui conviennent à quels moments?

Par programmation. L'expérience est le meilleur moyen d'apprendre.

C++ 11 a beaucoup de nouvelles fonctionnalités (auto, rvalue, nouveaux pointeurs intelligents - pour n'en nommer que quelques-uns). Le meilleur début est de commencer à les utiliser et de les lire chaque fois que vous le pouvez et chaque fois que vous trouvez un article intéressant.

Est-il vrai que l'ingénierie logicielle en C++ est aujourd'hui largement exempte de gestion manuelle de la mémoire?

Cela dépend de ce que vous devez faire. La plupart des applications peuvent s'en tirer avec des pointeurs intelligents et oublier la gestion de la mémoire. Il existe encore des applications qui ne peuvent pas s'échapper si facilement (par exemple, si elles ont besoin d'un nouveau placement ou d'un allocateur de mémoire personnalisé pour une raison quelconque).

Si vous devez utiliser Qt, vous devrez utiliser leurs règles de gestion de la mémoire.

quel compilateur dois-je utiliser pour tirer le meilleur parti de la nouvelle norme?

Tout ce que vous avez sous la main qui prend en charge la dernière norme:

mais aucun compilateur ne prend en charge toutes les fonctionnalités.

3
BЈовић