Arrière-Plan / Vue D'Ensemble
Opérations sur les variables automatiques ("à partir de la pile", qui sont des variables que vous créez sans appel malloc
/ new
) sont en général beaucoup plus rapide que celles impliquant les gratuit store ("le tas", qui sont des variables qui sont créés à l'aide de new
). Toutefois, la taille de automatique de tableaux est fixé au moment de la compilation, mais la taille des tableaux à partir de la boutique n'est pas. En outre, la taille de la pile est limitée (typiquement quelques MiB), alors que la boutique est uniquement limité par la mémoire de votre système.
Le SSO est le Court / Petite Optimisation de la Chaîne. Un std::string
généralement des magasins de la chaîne comme un pointeur vers le free store ("tas"), qui donne des caractéristiques de performance similaires si vous vous appelez new char [size]
. Cela empêche un débordement de pile pour les très grandes chaînes, mais il peut être plus lent, surtout avec les opérations de copie. Comme d'optimisation, de nombreuses implémentations de std::string
créer un petit automatique de tableau, quelque chose comme char [20]
. Si vous avez une chaîne de 20 caractères ou moins (étant donné cet exemple, la taille réelle varie), il stocke directement dans ce tableau. Cela évite la nécessité de convoquer new
, ce qui accélère les choses un peu.
EDIT:
Je ne m'attendais pas à cette réponse pour être tout à fait aussi populaire, mais comme il est, permettez-moi de donner une vision plus réaliste de la mise en œuvre, avec la réserve que je n'ai jamais lu aucun de mise en œuvre de l'authentification unique "dans la nature".
Détails de mise en œuvre
Au minimum, un std::string
des besoins de stocker les informations suivantes:
- La taille
- La capacité
- L'emplacement des données
La taille peut être stocké en tant que std::string::size_type
ou comme un pointeur vers la fin. La seule différence est que vous voulez avoir à la soustraction de deux pointeurs lorsque l'utilisateur appelle size
ou ajouter un size_type
d'un pointeur lorsque l'utilisateur appelle end
. La capacité peut être stocké en soit ainsi.
Vous ne payez pas pour ce que vous n'utilisez pas.
Tout d'abord, considérer l'implémentation naïve selon ce que j'ai décrit ci-dessus:
class string {
public:
// all 83 member functions
private:
std::unique_ptr<char[]> m_data;
size_type m_size;
size_type m_capacity;
std::array<char, 16> m_sso;
};
Pour un système 64 bits, ce qui signifie généralement qu' std::string
a 24 octets de "généraux" par la chaîne, plus un autre de 16 pour le SSO tampon (16 choisi ici au lieu de 20 en raison de rembourrage). Il n'aurait pas vraiment de sens pour stocker ces trois membres et d'un local de tableau de caractères, comme dans mon exemple simplifié. Si m_size <= 16
, alors je vais mettre toutes les données en m_sso
, donc je connais déjà la capacité et je n'ai pas besoin de le pointeur vers les données. Si m_size > 16
, alors je n'ai pas besoin m_sso
. Il n'y a absolument pas de chevauchement où j'ai besoin de tous. Une solution plus intelligente que ne gaspille pas de l'espace devrait ressembler à quelque chose d'un peu plus comme ça (pas testé, exemple seulement):
class string {
public:
// all 83 member functions
private:
size_type m_size;
union {
class {
// This is probably better designed as an array-like class
std::unique_ptr<char[]> m_data;
size_type m_capacity;
} m_large;
std::array<char, sizeof(m_large)> m_small;
};
};
Je suppose que la plupart des implémentations ressembler à ceci.