Quelqu'un a posté dans un commentaire à une autre question sur le sens de l' explicit
mot-clé en C++. Donc, ça veut dire quoi?
Réponses
Trop de publicités?En C++, le compilateur est autorisé à faire une conversion implicite pour régler les paramètres d'une fonction. Ce que cela signifie, c'est que le compilateur peut utiliser seul paramètre constructeurs pour convertir d'un type à l'autre afin d'obtenir le bon type d'un paramètre. Voici un exemple de classe avec un constructeur qui peut être utilisé pour les conversions implicites:
class Foo
{
public:
// single parameter constructor, can be used as an implicit conversion
Foo (int foo) : m_foo (foo)
{
}
int GetFoo () { return m_foo; }
private:
int m_foo;
};
Voici une fonction qui prend un Foo
objet:
void DoBar (Foo foo)
{
int i = foo.GetFoo ();
}
et c'est ici où la DoBar
fonction est appelée.
int main ()
{
DoBar (42);
}
Le paramètre n'est pas un Foo
objet, mais un int
. Cependant, il existe un constructeur Foo
qui prend un int
donc ce constructeur peut être utilisé pour convertir le paramètre le type correct.
Le compilateur est autorisé à le faire une fois pour chaque paramètre.
Le préfixe explicit
mot-clé pour le constructeur empêche le compilateur d'utiliser le constructeur pour les conversions implicites. Ajouter à la classe ci-dessus va créer une erreur de compilation, à l'appel de la fonction DoBar (42)
. Il est maintenant nécessaire de faire appel à la conversion explicitement DoBar (Foo (42))
La raison pour laquelle vous pourriez vouloir faire cela est pour éviter les blessures accidentelles de construction qui peuvent cacher des bugs. Exemple artificiel:
- Vous avez un
MyString(int size)
classe avec un constructeur qui construit une chaîne de taille donnée. Vous avez une fonctionprint(const MyString&)
, et vous l'appelez avecprint(3)
. Vous vous attendez à imprimer "3", mais il imprime une chaîne de caractères vide de longueur 3 à la place.
Supposons que vous avez une Chaîne de classe
class String {
public:
String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
}
Maintenant, si vous essayez
String mystring = 'x';
le caractère " x " seront convertis en int et fera appel à Cordes(int) constructeur. Mais ce n'est pas ce que l'utilisateur pourrait avoir prévu. Afin de prévenir de telles conditions, on peut définir le constructeur de la classe la plus explicite.
class String {
public:
explicit String (int n); //allocate n bytes
String(const char *p); // initialize sobject with string p
}
En C++, un constructeur avec un seul paramètre obligatoire est considéré comme une conversion implicite de la fonction. Il convertit le paramètre de type de la classe type. Si c'est une bonne chose ou pas dépend de la sémantique du constructeur.
Par exemple, si vous avez une chaîne de classe avec un constructeur String(const char* s)
, c'est probablement exactement ce que vous voulez. Vous pouvez passer une const char*
à une fonction attend un String
, et le compilateur va construire automatiquement une temporaire String
objet pour vous.
D'autre part, si vous avez un tampon de classe dont le constructeur Buffer(int size)
prend la taille de la mémoire tampon en octets, vous ne voulez probablement pas le compilateur tranquillement tourner int
s dans Buffer
s. Pour éviter cela, vous déclarer le constructeur avec l' explicit
mot-clé:
class Buffer { explicit Buffer(int size); ... }
De cette façon,
void useBuffer(Buffer& buf);
useBuffer(4);
devient une erreur de compilation. Si vous souhaitez passer un temporaire Buffer
objet, vous devez le faire de manière explicite:
useBuffer(Buffer(4));
En résumé, si votre seul paramètre du constructeur convertit le paramètre un objet de votre classe, vous ne voulez probablement pas à utiliser l' explicit
mot-clé. Mais si vous avez un constructeur qui prendre qu'un seul paramètre, vous devez le déclarer comme explicit
pour empêcher le compilateur de vous surprendre avec des conversions inattendues.
Cette réponse est à propos de la création d'un objet avec/sans explicitement un constructeur, car il n'est pas couvert dans les autres réponses.
Considérez les points suivants de la classe sans un constructeur explicite:
class Foo
{
public:
Foo(int x) : m_x(x)
{
}
private:
int m_x;
};
Les objets de la classe Foo peuvent être créés de 2 façons:
Foo bar1(10);
Foo bar2 = 20;
En fonction de la mise en œuvre, la seconde manière de l'instanciation de la classe Foo peut être source de confusion, ou pas ce que le programmeur a prévu. Le préfixe explicit
mot-clé pour le constructeur serait de générer une erreur de compilation, en Foo bar2 = 20;
.
Il est généralement une bonne pratique de déclarer seul argument des constructeurs comme explicit
, à moins que votre mise en œuvre spécifiquement interdit.
Notez également que les constructeurs avec
- les arguments par défaut pour tous les paramètres, ou
- les arguments par défaut pour le deuxième paramètre à partir de
peut à la fois être utilisé comme unique argument des constructeurs. De sorte que vous pouvez faire ces aussi explicit
.
Un exemple lorsque vous auraient délibérément de ne pas vous voulez faire de votre unique argument du constructeur explicite est si vous êtes à la création d'un foncteur (regardez le "add_x' struct a déclaré à cette réponse). Dans un tel cas, la création d'un objet en tant que add_x add30 = 30;
serait probablement logique.
Ici est un bon d'écrire-up sur explicite des constructeurs.