programas - ¿Por qué el constructor de copia de C++ debe usar el objeto const?
programacion c++ para principiantes pdf (8)
¡Lo último que cualquier consumidor de su clase esperaría es un constructor de copias que cambió el objeto que se copió! Por lo tanto, siempre debes marcar como const.
Entiendo que cuando definimos un constructor de copia de clase de la clase es necesario como regla de tres estados. También me doy cuenta de que el argumento del constructor de copia suele ser const
como lo ilustran los siguientes códigos:
class ABC {
public:
int a;
int b;
ABC(const ABC &other)
{
a = other.a;
b = other.b;
}
}
Mi pregunta es qué pasaría si el argumento del constructor de copia no es constante:
class ABC
{
public:
int a;
int b;
ABC(ABC &other)
{
a = other.a;
b = other.b;
}
}
Entiendo que en algunos casos, si el argumento del constructor de copia es constante, la segunda implementación fallará. Además, si el argumento del constructor de copia es constante, entonces el objeto a copiar no cambiará su contenido durante el proceso. Sin embargo, sí me doy cuenta de que algunas personas todavía usan la segunda implementación en lugar de la primera. ¿Hay alguna razón por la que se prefiera la segunda implementación?
Además de la suposición fundamental de que los constructores de copia no deben modificar la instancia de origen, este artículo explica la razón técnica real para usar const :
http://www.geeksforgeeks.org/copy-constructor-argument-const/
A saber eso y cito:
"... los objetos temporales creados por el compilador no se pueden vincular a referencias no constantes ..."
Como señalan otras respuestas, un constructor de copias que modifique su argumento sería una sorpresa desagradable. Ese no es, sin embargo, el único problema. Los constructores de copias se utilizan a veces con argumentos que son temporales . (Ejemplo: retorno de la función). Y las referencias no constantes a los temporales no vuelan, como se explica en elsewhere en SO.
Hay dos razones por las que se puede necesitar const:
- Asegura que no "dañe" accidentalmente el original al realizar la copia. Esto es algo bueno, porque realmente no desea que se cambie su objeto original cuando haga una copia.
- Puede pasar algo que no sea un objeto básico, ya que el constructor toma una referencia, si no es un objeto en sí mismo, por ejemplo, una expresión.
Para ejemplificar el segundo caso:
class ABC
{
public:
int a;
int b;
ABC(const ABC &other)
{
a = other.a;
b = other.b;
}
ABC operator+(const ABC &other)
{
ABC res;
res.a = a + other.a;
res.b = b + other.b;
return res;
}
}
...
ABC A;
a.a = 1;
a.b = 2;
ABC B(a+a);
Esto no se compilará si el constructor es ABC(ABC &other)
, ya que a+a
es un objeto temporal de tipo ABC. Pero si es ABC(const ABC &other)
, podemos usar el resultado temporal de un cálculo y aún así pasarlo como referencia.
Los constructores de copias no deben modificar el objeto del que se está copiando, por lo que se prefiere la const
en el other
parámetro. Ambos funcionarán, pero se prefiere la const
porque indica claramente que la función no debe modificar el objeto pasado.
const
es sólo para el usuario. No existe para el ejecutable real.
No es un "deber" en el sentido técnico. Incluso tenemos tal bestia en el estándar, aunque quedó en desuso . Siga los enlaces para el razonamiento.
La semántica de la copia que esperamos es dejar la "plantilla" sin cambios y proporcionar un clon que sea un equivalente exacto en todos los aspectos, que le sea difícil decirle al original.
Como es de esperar, deberás pensar dos veces en tener un ctor de copia que haga lo contrario. Sorprenderá a los usuarios y probablemente introducirá errores. Y la frustración y el ruido, solo intenta buscar en Google el ''vector de auto_ptr'' solo para ver el conteo.
El resto de la pregunta podría ser "Juro no tocar el original en la implementación, pero quiero una firma diferente". ¿Qué firma entonces? Probemos T y T &.
T se retira, ya que requeriría copiar ctor para poder usarse y estamos implementando precisamente eso. Recursión: ver recursión .
Eso deja a T &. Eso realmente funcionaría para muchos casos. Pero simplemente falle si su objeto original pasa a estar sentado en una forma constante, o si es temporal. ¿Por qué obstaculizar ese caso razonable para no llover en absoluto?
Si el constructor de copia no especifica su parámetro como const, este fragmento no se compilará.
const ABC foo;
ABC bar(foo);
Lógicamente, no debería tener sentido modificar un objeto del que solo desea hacer una copia, aunque a veces puede tener algún sentido, como una situación en la que le gustaría almacenar el número de veces que se ha copiado este objeto. Pero esto podría funcionar con una variable miembro
mutable
que almacena esta información, y puede modificarse incluso para un objeto const (y el segundo punto justificará este enfoque)Le gustaría poder crear una copia de objetos const. Pero si no está pasando su argumento con un calificador const, entonces no puede crear copias de objetos const ...
No se pudieron crear copias a partir de referencias temporales, porque los objetos temporales tienen un valor de r, y no pueden vincularse con referencias a no constantes. Para una explicación más detallada, sugiero el artículo de Herb Sutter al respecto.