Pensar en C++ (Volumen 1) - Grupo ARCO

Pensar en C++ (Volumen 1) - Grupo ARCO Pensar en C++ (Volumen 1) - Grupo ARCO

arco.esi.uclm.es
from arco.esi.uclm.es More from this publisher
13.01.2015 Views

✐ ✐ ✐ “Volumen1” — 2012/1/12 — 13:52 — page 232 — #270 ✐ Capítulo 8. Constantes // Returning consts by value // has no meaning for built-in types int f3() { return 1; } const int f4() { return 1; } int main() { const int j = f3(); // Works fine int k = f4(); // But this works fine too! } ///:~ Para los tipos básicos, no importa si el retorno es constante, así que debería evitar la confusión para el programador cliente y no utilizar const cuando se devuelven variables de tipos básicos por valor. Devolver por valor como constante se vuelve importante cuando se trata con tipos definidos por el programador. Si una función devuelve un objeto por valor como constante, el valor de retorno de la función no puede ser un recipiente 2 Por ejemplo: //: C08:ConstReturnValues.cpp // Constant return by value // Result cannot be used as an lvalue class X { int i; public: X(int ii = 0); void modify(); }; X::X(int ii) { i = ii; } void X::modify() { i++; } X f5() { return X(); } const X f6() { return X(); } void f7(X& x) { // Pass by non-const reference x.modify(); } int main() { f5() = X(1); // OK -- non-const return value f5().modify(); // OK //! f7(f5()); // Causes warning or error // Causes compile-time errors: //! f6() = X(1); //! f6().modify(); 2 N. del T.: «recipiente» corresponde con el término lvalue que se refiere a una variable que puede ser modificada o a la que se le puede asignar un valor. 232 ✐ ✐ ✐ ✐

✐ ✐ ✐ “Volumen1” — 2012/1/12 — 13:52 — page 233 — #271 ✐ 8.3. Argumentos de funciones y valores de retorno //! f7(f6()); } ///:~ f5() devuelve un objeto de clase X no constante, mientras que f6() devuelve un objeto de clase X pero constante. Solo el valor de retorno por valor no constante se puede usar como recipiente. Por eso, es importante usar const cuando se devuelve un objeto por valor si quiere impedir que se use como recipiente. La razón por la que const no tiene sentido cuando se usa para devolver por valor variables de tipos del lenguaje es que el compilador impide automáticamente el uso de dichos tipos como recipiente, ya que devuelve un valor, no una variable. Solo cuando se devuelven objetos por valor de tipos definidos por el programador esta funcionalidad toma sentido. La función f7() toma como argumento una referencia no constante (la referencia es una forma adicional para manejar direcciones en C++ y se trata en el [FIX- ME:XREF:capitulo 11]). Es parecido a tomar un puntero no constante, aunque la sintaxis es diferente. La razón por la que no compila es por la creación de un temporario. Temporarios A veces, durante la evaluación de una expresión, el compilador debe crear objetos temporales (temporarios). Son objetos como cualquier otro: requieren espacio de almacenamiento y se deben construir y destruir. La diferencia es que nunca se ven, el compilador es el responsable de decidir si se necesitan y los detalles de su existencia. Una particularidad importante de los temporarios es que siempre son constantes. Como normalmente no manejará objetos temporarios, hacer algo que cambie un temporario es casi seguro un error porque no será capaz de usar esa información. Para evitar esto, el compilador crea todos los temporarios como objetos constantes, de modo que le avisará si intenta modificarlos. En el ejemplo anterior, f5() devuelve un objeto no constante. Pero en la expresión: f7(f5()); el compilador debe crear un temporario para albergar el valor de retorno de f- 5() para que pueda ser pasado a f7(). Esto funcionaría bien si f7() tomara su argumento por valor; entonces el temporario se copiaría en f7() y no importaría lo que se pase al temporario X. Sin embargo, f7() toma su argumento por referencia, lo que significa que toma la dirección del temporario X. Como f7() no toma su argumento por referencia constante, tiene permiso para modificar el objeto temporario. Pero el compilador sabe que el temporario desaparecerá en cuanto se complete la evaluación de la expresión, y por eso cualquier modificación hecha en el temporario se perderá. Haciendo que los objetos temporarios sean constantes automáticamente, la situación causa un error de compilación de modo que evitará cometer un error muy difícil de localizar. En cualquier caso, tenga presente que las expresiones siguientes son correctas: f5() = X(1); f5().modify(); 233 ✐ ✐ ✐ ✐

✐<br />

✐<br />

✐<br />

“Volum<strong>en</strong>1” — 2012/1/12 — 13:52 — page 233 — #271<br />

✐<br />

8.3. Argum<strong>en</strong>tos de funciones y valores de retorno<br />

//! f7(f6());<br />

} ///:~<br />

f5() devuelve un objeto de clase X no constante, mi<strong>en</strong>tras que f6() devuelve<br />

un objeto de clase X pero constante. Solo el valor de retorno por valor no constante<br />

se puede usar como recipi<strong>en</strong>te.<br />

Por eso, es importante usar const cuando se devuelve un objeto por valor si<br />

quiere impedir que se use como recipi<strong>en</strong>te.<br />

La razón por la que const no ti<strong>en</strong>e s<strong>en</strong>tido cuando se usa para devolver por<br />

valor variables de tipos del l<strong>en</strong>guaje es que el compilador impide automáticam<strong>en</strong>te<br />

el uso de dichos tipos como recipi<strong>en</strong>te, ya que devuelve un valor, no una variable.<br />

Solo cuando se devuelv<strong>en</strong> objetos por valor de tipos definidos por el programador<br />

esta funcionalidad toma s<strong>en</strong>tido.<br />

La función f7() toma como argum<strong>en</strong>to una refer<strong>en</strong>cia no constante (la refer<strong>en</strong>cia<br />

es una forma adicional para manejar direcciones <strong>en</strong> <strong>C++</strong> y se trata <strong>en</strong> el [FIX-<br />

ME:XREF:capitulo 11]). Es parecido a tomar un puntero no constante, aunque la sintaxis<br />

es difer<strong>en</strong>te. La razón por la que no compila es por la creación de un temporario.<br />

Temporarios<br />

A veces, durante la evaluación de una expresión, el compilador debe crear objetos<br />

temporales (temporarios). Son objetos como cualquier otro: requier<strong>en</strong> espacio de<br />

almac<strong>en</strong>ami<strong>en</strong>to y se deb<strong>en</strong> construir y destruir. La difer<strong>en</strong>cia es que nunca se v<strong>en</strong>,<br />

el compilador es el responsable de decidir si se necesitan y los detalles de su exist<strong>en</strong>cia.<br />

Una particularidad importante de los temporarios es que siempre son constantes.<br />

Como normalm<strong>en</strong>te no manejará objetos temporarios, hacer algo que cambie<br />

un temporario es casi seguro un error porque no será capaz de usar esa información.<br />

Para evitar esto, el compilador crea todos los temporarios como objetos constantes,<br />

de modo que le avisará si int<strong>en</strong>ta modificarlos.<br />

En el ejemplo anterior, f5() devuelve un objeto no constante. Pero <strong>en</strong> la expresión:<br />

f7(f5());<br />

el compilador debe crear un temporario para albergar el valor de retorno de f-<br />

5() para que pueda ser pasado a f7(). Esto funcionaría bi<strong>en</strong> si f7() tomara su<br />

argum<strong>en</strong>to por valor; <strong>en</strong>tonces el temporario se copiaría <strong>en</strong> f7() y no importaría lo<br />

que se pase al temporario X.<br />

Sin embargo, f7() toma su argum<strong>en</strong>to por refer<strong>en</strong>cia, lo que significa que toma<br />

la dirección del temporario X. Como f7() no toma su argum<strong>en</strong>to por refer<strong>en</strong>cia<br />

constante, ti<strong>en</strong>e permiso para modificar el objeto temporario. Pero el compilador sabe<br />

que el temporario desaparecerá <strong>en</strong> cuanto se complete la evaluación de la expresión,<br />

y por eso cualquier modificación hecha <strong>en</strong> el temporario se perderá. Haci<strong>en</strong>do<br />

que los objetos temporarios sean constantes automáticam<strong>en</strong>te, la situación causa un<br />

error de compilación de modo que evitará cometer un error muy difícil de localizar.<br />

En cualquier caso, t<strong>en</strong>ga pres<strong>en</strong>te que las expresiones sigui<strong>en</strong>tes son correctas:<br />

f5() = X(1);<br />

f5().modify();<br />

233<br />

✐<br />

✐<br />

✐<br />

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!