2009-09-05 19 views
28

Por favor, ayúdenme a entender cómo funcionan exactamente los operadores de conversión en C++. Tengo un ejemplo simple que trato de entender, aunque no está muy claro cómo el compilador realmente realiza la conversión.Operadores de conversión 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: Me he dado cuenta que ha 'using namespace std;' cerca de la parte superior del archivo. Supongo que su profesor o material de enseñanza lo tiene así. Por casualidad no lo necesita en este ejemplo, pero tenga cuidado donde lo usa. ** Nunca ** nunca lo use en un archivo de cabecera; puede causar un dolor indescriptible si desea usar nombres que se encuentren en el espacio de nombres 'std'. Considere nunca usarlo en absoluto, pero si es como yo, puede usarlo solo en módulos (archivos .cpp) y, después de todo, #includes, pero antes de las definiciones de funciones y métodos. – quamrana

+0

¿Cuál es tu pregunta? –

Respuesta

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 compilador soporta copiar optimización constructor y optimización valor de retorno no se dará cuenta

Example(const Example&) 

ejecución, pero puede declarar constructor de copia privada que entender lo que estoy hablando acerca de.

+2

Ejemplo (int) no es un constructor de copia. –

+0

El ejemplo (int) no es, pero Example example = int is. Utiliza constructor de copia creado implícitamente, ¿no? –

+3

Copiar ctor tomaría [const] Example & como primer argumento. En este caso, el compilador llama implícitamente a Example (int), que es un ctor normal con un argumento: http://codepad.org/cGZ6YgT2 –

9

Puede recorrer ese código con un depurador (y/o poner un punto de interrupción en cada uno de sus constructores y operadores) para ver cuál de sus constructores y operadores está siendo invocado por qué líneas.

Como no los definió explícitamente, el compilador también creó un constructor de copias oculto/predeterminado y un operador de asignación para su clase. Puede definirlos explícitamente (como se muestra a continuación) si desea usar un depurador para ver dónde/cuándo se están llamando.

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

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

¿Sabes cómo desactivar RVO? –

+0

Las siguientes dos líneas me están matando ... Ejemplo exObject = theInt; int theInt1 = ctr; Se llama a un constructor de copia en este programa y qué tal el operador de asignación. Ninguno de ellos parece ser llamado. – Zuzu

2
Example exObject = theInt; // here 

Esto utiliza conversión implícita de int al ejemplo, efectuada por el constructor no explícita que acepta un int.

Esto también requiere la disponibilidad del constructor de copias para Ejemplo, aunque el compilador puede omitir la copia de la instancia.

int theInt1 = ctr; // here 

Esto usa la conversión implícita de Ejemplo a unsigned int, proporcionado por el operador de conversión.

Normalmente se evitan los operadores de ediciones, ya que tienden a generar códigos confusos, y puede marcar los constructores de argumentos únicos de forma explícita, para deshabilitar las conversiones implícitas a su tipo de clase. C++ 0x debería agregar también la posibilidad de marcar explícitamente los operadores de conversión (por lo que necesitaría un static_cast para invocarlos? - mi compilador no los admite y todos los recursos web parecen concentrarse en la conversión explícita a 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; 
} 

En la instrucción 1 se llama al constructor Example(int val). Declararlo como explicit Example(int val) y obtendrá un error de tiempo de compilación, es decir, no se permitirá ninguna conversión implícita para este constructor.

Todos los constructores de un único argumento se llaman implícitamente si el valor asignado es de su tipo de argumento respectivo. El uso de la palabra clave explicit antes de los constructores de un solo argumento inhabilita la invocación implícita del constructor y, por lo tanto, la conversión implícita.

Si el constructor se declaró explícito, es decir, explicit Example(int val), sucedería lo siguiente para cada instrucción.

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

También tenga en cuenta que en caso de llamada al constructor implícito y la conversión por lo tanto implícito el valor asignado es un valor p es decir,un objeto sin nombre implícitamente creado usando un valor-i (Theint) que nos dice que en el caso de la conversión implícita del compilador convierte

Example exObject = theInt; 

a

Example exObject = Example(theInt); 

Por lo tanto (en C++ 11) Don' Espere que se llame al constructor lvalue, ya que está utilizando un lvalue, es decir, un valor con nombre theInt para la asignación. Lo que se llama es el constructor de rvalue ya que el valor asignado es en realidad el objeto sin nombre creado utilizando lvalue. Sin embargo, esto se aplica si tiene versiones lvalue y rvalue del constructor.

En la declaración 2 operator unsigned int() se llama. Simplemente considérelo como una llamada a función normal con un nombre extraño y el hecho de que se puede llamar automágicamente cuando ocurre una conversión implícita. El valor devuelto por esa función es el valor asignado en la expresión. Y dado que en su implementación, el valor devuelto es una int, se asigna correctamente a int theInt1.

Para ser precisos operator unsigned int() sobrecargas () operador que es el operador de reparto. En su caso, está sobrecargado para int, por lo tanto, cada vez que se asigna un objeto de la clase Example a int, se produce el vaciado de tipo implícito de Example a int y, por lo tanto, se llama a operator unsigned int(). Por lo tanto,

int theInt1 = ctr; 

es equivalente a

int theInt1 = (int)ctr; 
+1

Ejemplo exObject (theInt); incluso si el constructor es explícito, no creo que deba ser un error de tiempo de compilación. La inicialización directa está permitida. – bornfree

+0

Esto es impar: 'Example exObject (Example (theInt));'. ¿Estás seguro de que es correcto? Tal vez tu intención fue escribir 'Example exObject ((Ejemplo) theInt);' – mathiasfk