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.
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:
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 erase
– remove_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);
Comme point de départ:
char*
pour les chaînes. Utilisation std::string
ou std::wstring
et regardez votre code devenir plus court, plus lisible et plus sûr[ ]
) 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.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 queauto
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
)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.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
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 (#define
stuff), 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.
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.