2009-09-05 10 views
28

Veuillez m'aider à comprendre comment fonctionnent exactement les opérateurs de conversion en C++. J'ai ici un exemple simple que j'essaie de comprendre, bien qu'il ne soit pas très clair comment la conversion se passe réellement par le compilateur.Opérateurs de conversion en C++

class Example{ 
public: 
    Example(); 
    Example(int val); 
    operator unsigned int(); 
    ~Example(){} 
private: 
    int itsVal; 
}; 

Example::Example():itsVal(0){} 

Example::Example(int val):itsVal(val){} 

Example::operator unsigned int(){ 
    return (itsVal); 
} 

int main(){ 
    int theInt = 5; 
    Example exObject = theInt; // here 
    Example ctr(5); 
    int theInt1 = ctr; // here 
    return 0; 
} 
+16

@Zuzu: Je remarque que vous avez 'using namespace std;' vers le haut du fichier. Je suppose que votre conférencier ou le matériel d'enseignement l'a comme ça. Vous n'arrivez pas à en avoir besoin dans cet exemple, mais soyez prudent lorsque vous l'utilisez. ** Jamais ** jamais l'utiliser dans un fichier d'en-tête - cela peut causer un chagrin incalculable si vous voulez utiliser vous-même des noms qui se trouvent dans l'espace de noms 'std'. Envisagez de ne jamais l'utiliser du tout, mais si vous êtes comme moi, vous pouvez l'utiliser uniquement dans les modules (fichiers .cpp) et après tout #includes, mais avant les définitions de fonction et de méthode. – quamrana

+0

Quelle est votre question? –

Répondre

4
Example exObject = theInt; // implicitly created copy constructor takes place 
// object implicitly created from int and then copied 
// it is like 
Example exObject = Example(theInt); 
// so it uses sequence 
// Example(int) -> Example(const Example&) 
int theInt1 = ctr; // operator int() 

Si vous compilateur supporte la copie d'optimisation du constructeur et de l'optimisation de la valeur de retour, vous ne remarquerez pas

Example(const Example&) 

exécution, mais vous pouvez déclarer constructeur de copie être privé de comprendre ce dont je parle sur.

+2

Exemple (int) n'est pas un constructeur de copie. –

+0

L'exemple (int) ne l'est pas, mais Exemple exemple = int est. Il utilise le constructeur de copie créé implicitement, non? –

+3

Copier ctor prendrait [const] Exemple & comme premier argument. Dans ce cas, le compilateur appelle implicitement Example (int), qui est un ctor normal avec un argument: http://codepad.org/cGZ6YgT2 –

9

Vous pouvez parcourir ce code avec un débogueur (et/ou placer un point d'arrêt sur chacun de vos constructeurs et opérateurs) pour voir lequel de vos constructeurs et opérateurs est appelé par quelles lignes. Comme vous ne les avez pas définis explicitement, le compilateur a également créé un constructeur de copie/par défaut/opérateur d'assignation par défaut pour votre classe. Vous pouvez les définir explicitement (comme suit) si vous souhaitez utiliser un débogueur pour voir où/quand ils sont appelés.

Example::Example(const Example& rhs) 
: itsVal(rhs.itsVal) 
{} 

Example& operator=(const Example& rhs) 
{ 
    if (this != &rhs) 
    { 
     this->itsVal = rhs.itsVal; 
    } 
    return *this; 
} 
+0

Savez-vous comment désactiver RVO? –

+0

Les deux lignes suivantes me détruisent. Exemple exObject = theInt; int theInt1 = ctr; Est-ce qu'un constructeur de copie est appelé dans ce programme et qu'en est-il de l'opérateur d'affectation? Aucun d'entre eux ne semble être appelé. – Zuzu

2
Example exObject = theInt; // here 

Il utilise la conversion implicite de int à l'exemple, effectué par le constructeur non explicite qui accepte un int.

Cela nécessite également la disponibilité du constructeur de copie pour Example, même si le compilateur est autorisé à omettre de copier l'instance.

int theInt1 = ctr; // here 

Ceci utilise la conversion implicite de l'exemple en un entier non signé, fournie par l'opérateur de conversion.

Les opérateurs Cast sont normalement évités, car ils ont tendance à conduire à un code prêtant à confusion, et vous pouvez marquer explicitement les constructeurs à un seul argument pour désactiver les conversions implicites dans votre type de classe. C++ 0x devrait aussi ajouter la possibilité de marquer explicitement les opérateurs de conversion (donc vous auriez besoin d'un static_cast pour les invoquer - mon compilateur ne les supporte pas et toutes les ressources web semblent se concentrer sur la conversion explicite en bool).

5
int main() { 
    int theInt = 5; 

    /** 
    * Constructor "Example(int val)" in effect at the statement below. 
    * Same as "Example exObject(theInt);" or "Example exObject = Example(theInt);" 
    */ 
    Example exObject = theInt; // 1 

    Example ctr(5); 

    /** 
    * "operator unsigned int()" in effect at the statement below. 
    * What gets assigned is the value returned by "operator unsigned int()". 
    */ 
    int theInt1 = ctr; // 2 

    return 0; 
} 

À l'instruction 1, le constructeur Example(int val) est appelé. Déclarez le comme explicit Example(int val) et vous obtiendrez une erreur de temps de compilation, c'est-à-dire qu'aucune conversion implicite ne sera autorisée pour ce constructeur.

Tous les constructeurs à un seul argument sont appelés implicitement si la valeur affectée est de leur type d'argument respectif. L'utilisation du mot clé explicit avant les constructeurs à un seul argument désactive l'appel du constructeur implicite et donc la conversion implicite.

Si le constructeur a été déclaré comme explicite, c'est-à-dire explicit Example(int val), alors ce qui suit arriverait pour chaque instruction.

Example exObject(theInt); // Compile time error. 
Example exObject = theInt; // Compile time error. 
Example exObject(Example(theInt)); // Okay! 
Example exObject = Example(theInt); // Okay! 

Notez également qu'en cas d'appel du constructeur implicite et donc la conversion implicite la valeur attribuée est une rvalue à savoirun objet non nommé implicitement créé à l'aide d'un lvalue (Theint) qui nous dit qu'en cas de conversion implicite le compilateur convertit

Example exObject = theInt; 

à

Example exObject = Example(theInt); 

donc (en C de 11) n » Attendez-vous à ce que le constructeur de lvalue soit appelé vu que vous utilisez une valeur lvalue, c'est-à-dire une valeur nommée theInt pour l'affectation. Ce qui s'appelle est le constructeur rvalue puisque la valeur assignée est en fait l'objet non nommé créé en utilisant le lvalue. Cependant, ceci s'applique si vous avez à la fois des versions lvalue et rvalue du constructeur. Sur l'instruction 2, operator unsigned int() est appelée. Il suffit de le considérer comme un appel de fonction normal avec un nom bizarre et le fait qu'il peut être appelé automagiquement lorsqu'une conversion implicite se produit. La valeur renvoyée par cette fonction est la valeur affectée dans l'expression. Et puisque dans votre implémentation, la valeur renvoyée est un entier correctement affecté à int theInt1.

Pour être précis operator unsigned int() surcharges () opérateur qui est l'opérateur de moulage. Dans votre cas, il est surchargé pour int donc chaque fois qu'un objet de la classe Example est affecté à un int, la transtypage de type implicite de Example à int a lieu et operator unsigned int() est appelée. Par conséquent,

int theInt1 = ctr; 

est équivalent à

int theInt1 = (int)ctr; 
+1

Exemple exObject (theInt); même si le constructeur est explicite, je ne pense pas que ce soit une erreur de compilation. L'initialisation directe est autorisée. – bornfree

+0

Ceci est étrange: 'Exemple exObject (Exemple (theInt));'. Êtes-vous sûr que c'est juste? Peut-être que vous aviez l'intention d'écrire 'Exemple exObject ((Exemple) theInt);' – mathiasfk