web-dev-qa-db-fra.com

c ++ std :: ostringstream vs std :: string :: append

Dans tous les exemples qui utilisent une sorte de mise en mémoire tampon, je vois qu'ils utilisent stream au lieu de chaîne. En quoi std :: ostringstream et << operator sont différents de l'utilisation de string.append. Lequel est le plus rapide et lequel utilise le moins de ressources (mémoire).

Une différence que je connais est que vous pouvez générer différents types dans le flux de sortie (comme un entier) plutôt que les types limités que string :: append accepte.

Voici un exemple:

std::ostringstream os;
os << "Content-Type: " << contentType << ";charset=" << charset << "\r\n";
std::string header = os.str();

contre

std::string header("Content-Type: ");
header.append(contentType);
header.append(";charset=");
header.append(charset);
header.append("\r\n");

Évidemment, utiliser stream est plus court, mais je pense que append renvoie la référence à la chaîne pour qu'elle puisse être écrite comme suit:

std::string header("Content-Type: ");
header.append(contentType)
  .append(";charset=")
  .append(charset)
  .append("\r\n");

Et avec le flux de sortie, vous pouvez faire:

std::string content;
...
os << "Content-Length: " << content.length() << "\r\n";

Mais qu'en est-il de l'utilisation de la mémoire et de la vitesse? Surtout lorsqu'il est utilisé dans une grande boucle.

Mettre à jour:

Pour être plus clair, la question est la suivante: Lequel devrais-je utiliser et pourquoi? Y a-t-il des situations où l’un est préféré ou l’autre? Pour les performances et la mémoire ... eh bien, je pense que benchmark est le seul moyen puisque chaque implémentation peut être différente.

Mise à jour 2:

Eh bien, je ne comprends pas bien ce que je devrais utiliser à partir des réponses, ce qui signifie qu'aucun d'entre eux ne fera son travail, plus le vecteur. Cubbi a fait référence à Nice avec l’ajout de Dietmar Kühl: la plus grande différence est la construction de ces objets. Si vous cherchez une réponse, vous devriez aussi vérifier cela. J'attendrai un peu plus pour d'autres réponses (regardez la mise à jour précédente) et si je n'en ai pas, je vais accepter la réponse de Tolga car sa suggestion d'utiliser le vecteur est déjà faite avant, ce qui signifie que le vecteur devrait avoir moins faim de ressources.

26
NickSoft

std::ostringstream n'est pas nécessairement stocké sous forme de tableau séquentiel de caractères en mémoire. Lors de l'envoi de ces en-têtes HTTP, vous devez disposer d'un tableau continu de caractères afin de pouvoir copier/modifier le tampon interne pour le rendre séquentiel.

std::string à l'aide de std::string::reserve approprié n'a aucune raison d'agir plus lentement que std::ostringstream dans cette situation.

Cependant, std::ostringstream est probablement plus rapide pour l'ajout si vous n'avez absolument aucune idée de la taille que vous devez réserver. Si vous utilisez std::string et que votre chaîne s'agrandit, il faudra éventuellement réallouer et copier tout le tampon. Il serait préférable d’utiliser une std::ostringstream::str() pour rendre les données séquentielles à la fois, par rapport aux réallocations multiples qui se produiraient autrement.

P.S. Pre-C++ 11 std::string n'a pas besoin d'être séquentiel non plus, alors que presque toutes les bibliothèques l'implémentent en tant que séquentiel. Vous pouvez risquer cela ou utiliser std::vector<char> à la place. Vous devez utiliser ce qui suit pour faire des ajouts:

char str[] = ";charset=";
vector.insert(vector.end(), str, str + sizeof(str) - 1);

std::vector<char> serait préférable pour les performances car sa construction est probablement moins chère, mais ce n’est sans doute pas un facteur important par rapport à std::string et le temps qu’il faut réellement pour les construire. J'ai fait quelque chose de similaire à ce que vous essayez et suis allé avec std::vector<char> auparavant. Purement pour des raisons logiques; le vecteur semblait mieux correspondre au travail. Vous ne voulez pas réellement de manipulations de cordes ou autres. De plus, les tests de performance que j'ai effectués par la suite ont prouvé qu'il fonctionnait mieux ou peut-être que c'était uniquement parce que je n'avais pas mis en œuvre les opérations avec assez de précision avec std::string.

Lors du choix, le conteneur répondant à vos besoins et doté de fonctionnalités supplémentaires minimales fait généralement le meilleur travail.

10
Etherealone

construire un objet de flux est une opération nettement plus complexe que la construction d'un objet de chaîne, car il doit contenir (et donc construire) son membre std::locale, entre autres choses nécessaire pour maintenir l'état (mais la locale est par grande marge la plus lourde).

L'ajout est similaire: les deux gèrent un tableau de caractères contigu, les deux attribuent davantage lorsque la capacité est dépassée. La seule différence à laquelle je peux penser est que lors de l'ajout à un flux, il existe un seul appel de fonction membre virtuel par débordement (en plus de l'allocation/copie de mémoire, qui domine de toute façon le traitement du débordement), et operator<< doit effectuer des vérifications supplémentaires état du flux.

Notez également que vous appelez str (), qui copie la chaîne entière une fois de plus. Par conséquent, en fonction de la nature de votre code, l'exemple de flux en fait davantage et devrait être plus lent.

Testons:

#include <sstream>
#include <string>
#include <numeric>

volatile unsigned int sink;
std::string contentType(50, ' ');
std::string charset(50, ' ');
int main()
{
 for(long n = 0; n < 10000000; ++n)
 {
#ifdef TEST_STREAM    
    std::ostringstream os;
    os << "Content-Type: " << contentType << ";charset=" << charset << "\r\n";
    std::string header = os.str();
#endif
#ifdef TEST_STRING
    std::string header("Content-Type: ");
    header.append(contentType);
    header.append(";charset=");
    header.append(charset);
    header.append("\r\n");
#endif
    sink += std::accumulate(header.begin(), header.end(), 0);
 }
}

c'est 10 millions} _ répétitions

Sur mon Linux, je reçois

                   stream         string
g++ 4.8          7.9 seconds      4.4 seconds
clang++/libc++  11.3 seconds      3.3 seconds

ainsi, pour ce cas d'utilisation, dans ces deux implémentations, les chaînes semblent fonctionner plus rapidement, mais il est évident que les deux méthodes ont beaucoup à améliorer (reserve () la chaîne, déplacez la construction du flux hors de la boucle, utilisez un flux qui ne nécessite pas copier pour accéder à sa mémoire tampon, etc.)

20
Cubbi

Avec stream, votre classe Myclass peut remplacer l'opération << afin que vous puissiez écrire

MyClass x;
ostringstream y;
y << x;

Pour ajouter, vous devez avoir une méthode ToString (ou quelque chose de similaire) car vous ne pouvez pas remplacer la fonction d’ajout de chaîne.

Pour certains morceaux de code, utilisez ce qui vous convient le mieux . Utilisez stream pour les projets plus importants où il est utile de pouvoir simplement diffuser un objet.

1
Sorin

Si vous vous souciez de la vitesse, vous devriez profiler et/ou tester. En théorie, std::string::append ne devrait pas être plus lent car plus simple (le flux doit traiter les paramètres régionaux, un formatage différent et être plus générique). Mais à quelle vitesse une solution ou une autre est-elle réellement réalisable que vous ne pouvez réaliser qu'en testant.

0
Slava