Quand dois-je utiliser std::string
et quand dois-je utiliser char*
pour gérer les tableaux de char
s en C++?
Il semble que vous devriez utiliser char*
si les performances (vitesse) sont cruciales et que vous êtes prêt à accepter certaines activités risquées en raison de la gestion de la mémoire.
Y a-t-il d'autres scénarios à considérer?
Vous pouvez passer std :: strings par référence si elles sont grandes pour éviter la copie, ou un pointeur vers l'instance, donc je ne vois aucun avantage réel à utiliser des pointeurs char.
J'utilise std :: string/wstring pour plus ou moins tout ce qui est du texte réel. char *
est cependant utile pour d'autres types de données et vous pouvez être sûr qu'elles seront désallouées comme il se doit. Sinon, std :: vector est la voie à suivre.
Il y a probablement des exceptions à tout cela.
Mon point de vue est:
Oui, parfois vous pouvez vraiment faire ça. Lorsque vous utilisez const char *, les tableaux char alloués sur la pile et les littéraux de chaîne, vous pouvez le faire de manière à ce qu'il n'y ait aucune allocation de mémoire du tout.
L'écriture d'un tel code nécessite souvent plus de réflexion et d'attention que l'utilisation d'une chaîne ou d'un vecteur, mais avec des techniques appropriées, cela peut être fait. Avec des techniques appropriées, le code peut être sûr, mais vous devez toujours vous assurer que lors de la copie dans char [], vous avez soit des garanties sur la longueur de la chaîne copiée, soit vous vérifiez et gérez les chaînes surdimensionnées avec élégance. Ne pas le faire est ce qui a donné à la famille de fonctions strcpy la réputation d'être dangereuse.
En ce qui concerne la sécurité des tampons char [], les modèles peuvent aider, car ils peuvent créer une encapsulation pour gérer la taille du tampon pour vous. Des modèles comme celui-ci sont mis en œuvre, par exemple par Microsoft pour fournir des remplacements sûrs pour strcpy. L'exemple ici est extrait de mon propre code, le vrai code a beaucoup plus de méthodes, mais cela devrait suffire pour transmettre l'idée de base:
template <int Size>
class BString
{
char _data[Size];
public:
BString()
{
_data[0]=0;
// note: last character will always stay zero
// if not, overflow occurred
// all constructors should contain last element initialization
// so that it can be verified during destruction
_data[Size-1]=0;
}
const BString &operator = (const char *src)
{
strncpy(_data,src,Size-1);
return *this;
}
operator const char *() const {return _data;}
};
//! overloads that make conversion of C code easier
template <int Size>
inline const BString<Size> & strcpy(BString<Size> &dst, const char *src)
{
return dst = src;
}
Une occasion que vous DEVEZ utiliser char*
et pas std::string
est lorsque vous avez besoin de constantes de chaîne statiques. La raison en est que vous n'avez aucun contrôle sur les modules de commande qui initialisent leurs variables statiques, et un autre objet global d'un module différent peut faire référence à votre chaîne avant son initialisation. http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Static_and_Global_Variables
std::string
avantages:
std::string
inconvénients: - deux instances de chaîne STL distinctes ne peuvent pas partager le même tampon sous-jacent. Donc, si vous passez par valeur, vous obtenez toujours une nouvelle copie. - il y a une pénalité de performance, mais je dirais que si vos exigences ne sont pas spéciales, elle est négligeable.
Vous devriez envisager d'utiliser char*
dans les cas suivants:
En fait, en C++, char*
sont souvent utilisés pour les petits mots fixes, comme options, nom de fichier, etc ...
Quand utiliser une chaîne std :: c ++:
Quand utiliser char *
Utilisez (const) char * comme paramètres si vous écrivez une bibliothèque. Les implémentations std :: string diffèrent entre les différents compilateurs.
Si vous souhaitez utiliser des bibliothèques C, vous devrez vous occuper des chaînes C. Il en va de même si vous souhaitez exposer votre API à C.
Vous pouvez vous attendre à ce que la plupart des opérations sur une chaîne std :: (comme par exemple find
) soient aussi optimisées que possible, elles sont donc susceptibles d'effectuer au moins aussi bien qu'un homologue C pur.
Il convient également de noter que les itérateurs std :: string sont souvent mappés vers des pointeurs dans le tableau de caractères sous-jacent. Ainsi, tout algorithme que vous concevez au-dessus des itérateurs est essentiellement identique au même algorithme au-dessus de char * en termes de performances.
Les choses à surveiller sont par exemple operator[]
- la plupart des implémentations STL n'effectuent pas de vérification des limites et doivent traduire cela en la même opération sur le tableau de caractères sous-jacent. AFAIK STLPort peut éventuellement effectuer une vérification des limites, auquel cas cet opérateur serait un peu plus lent.
Alors, que vous apporte std :: string? Il vous dispense de la gestion manuelle de la mémoire; le redimensionnement du tableau devient plus facile, et vous devez généralement penser moins à libérer de la mémoire.
Si vous vous inquiétez des performances lors du redimensionnement d'une chaîne, il existe une fonction reserve
qui peut vous être utile.
si vous utilisez le tableau de caractères dans un texte similaire, etc. utilisez std :: string plus flexible et plus facile à utiliser. Si vous l'utilisez pour autre chose comme le stockage de données? utiliser des tableaux (préférez les vecteurs)
Même lorsque les performances sont cruciales, il vaut mieux utiliser vector<char>
- il permet l'allocation de mémoire à l'avance (méthode reserve ()) et vous aidera à éviter les fuites de mémoire. L'utilisation de vector :: operator [] conduit à une surcharge, mais vous pouvez toujours extraire l'adresse du tampon et l'indexer exactement comme s'il s'agissait d'un char *.