Est-il garanti par la norme que std::string
ne rendra pas la mémoire allouée spontanément si elle est réaffectée à partir d'une chaîne de taille plus petite?
En d'autres termes:
std::string str = "Some quite long string, which needs a lot of memory";
str = "";
str = "A new quite long but smaller string"; // Guaranteed to not result in a heap allocation?
Je demande parce que je compte sur cela pour éviter la fragmentation du tas.
[string.cons]/36
définit l'affectation d'un const char*
À un std::string
En terme d'affectation de déplacement, dont la définition est:
[string.cons]/32
basic_string& operator=(basic_string&& str) noexcept(/*...*/)
Effets : Déplacer les affectations en tant que conteneur de séquence, sauf que les itérateurs, pointeurs et références peuvent être invalidés.
Cela montre que le Comité a laissé la mise en œuvre choisir librement entre une opération invalidante et une opération plus conservatrice. Et pour rendre les choses encore plus claires:
[basic.string]/4
Les références, pointeurs et itérateurs faisant référence aux éléments d'une séquence
basic_string
Peuvent être invalidés par les utilisations suivantes de cet objet basic_string:
- (4.1) comme argument à toute fonction de bibliothèque standard en prenant comme référence un non-const
basic_string
.- (4.2) Appel de fonctions membres non const, sauf
operator[]
,at
,data
,front
,back
,begin
,rbegin
,end
etrend
.
Je demande parce que je compte sur cela pour éviter la fragmentation du tas.
std::string
Prend comme paramètre-modèle un allocateur. Si vous êtes vraiment préoccupé par une possible fragmentation du tas, vous pouvez écrire la vôtre, qui avec certaines heuristiques pourrait avoir une stratégie d'allocation adaptée à vos besoins.
Dans la pratique, la plupart des implémentations que je connais ne réalloueraient pas la mémoire dans le cas de votre question. Cela peut être vérifié en testant et/ou en vérifiant votre document d'implémentation et éventuellement le code source.
référence CPP indique que l'affectation à un pointeur vers caractère
Remplace le contenu par celui de la chaîne de caractères terminée par un caractère nul pointée par s comme si par * this = basic_string (s), ce qui implique un appel à Traits :: length (s).
Ce "comme si" se résume en fait à une affectation rvalue, donc le scénario suivant est tout à fait possible:
La norme garantit-elle que std :: string ne restituera pas spontanément la mémoire allouée si elle est réaffectée à partir d'une chaîne de taille plus petite?
Quelle que soit la réponse réelle (qui est "non, aucune garantie") - vous devez utiliser le principe suivant: S'il n'est pas évident que ce doit être le cas, alors ne le présumez pas est le cas.
Dans votre cas spécifique - si vous voulez contrôler étroitement le comportement du tas, vous ne voudrez peut-être même pas utiliser std::string
s du tout (peut-être; cela dépend). Et vous pourriez ne pas vouloir utiliser l'allocateur par défaut (encore une fois, peut-être); et vous voudrez peut-être mémoriser des chaînes ; etc. Ce que vous devez absolument faire, c'est faire moins d'hypothèses, mesurer si possible et avoir une conception explicite pour garantir que vos besoins sont satisfaits.
Si vos chaînes sont courtes (jusqu'à 15 ou 22 octets, selon le compilateur/la bibliothèque std) et que vous utilisez un compilateur relativement récent en C++ 11 ou en mode ultérieur, vous bénéficierez probablement de la Optimisation des chaînes courtes (SSO). Dans ce cas, le contenu de la chaîne n'est pas alloué séparément sur le tas.
Ce lien contient également de nombreux détails sur les implémentations communes et les stratégies d'allocation.
Cependant, les deux chaînes de votre exemple sont trop longues pour SSO.