Pregunta

Por favor conforme si estoy en lo correcto y dime si hay una mejor solución:

Yo entiendo que los objetos con los miembros constantes como int const width; no pueden ser manejados por el operador de asignación sintético que se crea de forma implícita por el compilador. Pero QList (y supongo que std :: lista, también) necesita un operador de asignación de trabajo. Así que cuando quiero utilizar objetos con los miembros constantes y QList tengo tres posibilidades:

  1. No utilice miembros constantes. (No una solución)
  2. Implementar mi propio operador de asignación.
  3. Utilice algún otro recipiente que no necesita la asignación operadores

¿Es eso correcto? ¿Hay otras soluciones elegantes?

También me pregunto si puedo:

  • (4) Fuerza el compilador para crear un operador de asignación que se ocupa de los miembros constantes! (No entiendo por qué esto es un problema tan grande. ¿Por qué el operador no lo suficientemente inteligente como para listas de inicialización uso interno? O me estoy perdiendo algo?)
  • (5) Dile a QList que nunca voy a usar operaciones de asignación en la lista.

Edit: No asigne nunca objetos de esta clase yo mismo. Sólo son creados por el constructor de copia o por un constructor sobrecargado. Por lo que el operador de asignación sólo es necesaria por el contenedor no por mí mismo.

Edit2: Este es el operador de asignación creé. No estoy seguro de si es correcta, aunque. Cell tiene un constructor de dos parámetros. Estos parámetros ajustan los dos miembros constantes con listas de inicialización. Pero el objeto contiene también otra variable (no const) miembros.

Cell& Cell::operator=(Cell const& other)
{
 if (this != &other) {
  Cell* newCell = new Cell(other.column(), other.row());
  return *newCell;
 }
 return *this;
}

Edit3: He encontrado este hilo con casi la misma pregunta: C ++: Problemas STL con miembros de la clase const Todas las respuestas combinadas junto respondido a mis preguntas.

¿Fue útil?

Solución 4

voy a tratar de agrupar las respuestas en corto:

El problema principal es que QList requiere que el operador de asignación de estar presente, ya que internamente usan asignación. De este modo se mezclan aplicación con interfaz. Así que a pesar de que no es necesario el operador de asignación QList no funcionará sin él.

@ 3. No se std :: lista pero no ofrece acceso en tiempo constante a los elementos, mientras que QList hace.

@ 2. Es posible mediante la creación de un nuevo objeto con el constructor de copia y las propiedades deseadas y devolviéndolo *. A pesar de que eludir la propiedad const todavía es mejor que no usar const en absoluto, ya que permitiría que el contenedor de hacer trampa aquí, pero todavía impide a los usuarios a hacerlo por sí mismos, que era la intención original de hacer esta constante miembro.

Sin embargo, tome en cuenta que la creación de un operador de asignación sobrecargado se suma a la complejidad del código y podría introducir más errores que el const-ción de los miembros resolvería en el primer lugar.

@ 1. Al final, esto parece ser la solución más fácil. Mientras que es privado sólo hay que prestar atención que el objeto no cambia ella misma.

@ 4. No hay manera de obligarlo. Él no sabría porque la variable es constante y en algún momento tendría que hacer this->row = other.row con int const row; definido previamente. Y medios const constante, incluso en este caso. una fuente

@ 5 QList no tiene opciones de este tipo.

soluciones adicionales:

  • Uso puntero a objetos en lugar de objetos puros

* No está seguro acerca de esto en el momento.

Otros consejos

Usted es probablemente un recién llegado a C ++ y espera que se comporte como Python, Java o C #.

Es muy común poner inmutable objetos Java en colecciones. Esto funciona porque en Java, que en realidad no ponen de Java objetos en colecciones, sino simplemente de Java referencias que se refieren a objetos Java. Para ser aún más preciso, una colección internamente consta de variables de referencia de Java, y asignar a estas variables de referencia de Java no afecta a los objetos Java que se hace referencia en absoluto. Hacen notificación ni siquiera.

I dijo deliberadamente "objeto Java", "Referencia de Java" y "variable de Java", porque los términos "objeto", "referencia" y "variable" tienen significados completamente diferentes en C ++. Si desea que las variables T mutables, desea que los objetos mutables T, ya que las variables y objetos son básicamente lo mismo en C ++:

  

Una variable se introduce por la declaración de un objeto. El nombre de la variable denota el objeto.

En C ++, las variables no contienen objetos - que son objetos . Asignación a un medio de variables que cambian el objeto (llamando a la función operator= miembro). No hay manera de evitarlo. Si usted tiene un objeto inmutable, entonces la asignación a = b no puede , posiblemente, el trabajo sin menoscabo de forma explícita el sistema de tipos, y si lo hace, entonces usted ha mentido de manera efectiva a sus clientes sobre el objeto de ser inmutable. Hacer una promesa y luego deliberadamente rompiendo es bastante inútil, ¿verdad?

Por supuesto, usted podría simplemente simular la forma en que Java: utilizar una colección de punteros a objetos inmutables. Si es o no es una solución eficaz depende de lo que sus objetos realmente representan. Pero sólo porque esto funciona bien en Java, no significa que funciona bien en C ++. No hay tal cosa como un patrón de valores objeto inmutable en C ++. Es una idea buena en Java y una terrible idea en C ++.

Por cierto, el operador de asignación es completamente no-idiomática y pérdidas de memoria. Si usted es serio sobre el aprendizaje de C ++, debe leer .

(4) no es una opción. Los cesionarios operador de asignación de copia implícitamente declaradas cada miembro del objeto lado derecho al mismo miembro del objeto lado izquierdo.

El compilador no puede generar de forma implícita un operador de asignación de copia para una clase que tiene miembros de datos const-calificados por la misma razón que esto no es válido:

const int i = 1;
i = 2;

(2) es problemático ya que hay que superar esta misma edición de alguna manera.

(1) es la solución obvia; Si el tipo de clase tiene miembros de datos const-calificados, no es asignable y asignación no tiene mucho sentido. ¿Por qué dice que esto no es una solución?


Si no desea que su tipo de clase que sea asignable a continuación, no se puede utilizar en un recipiente que requiere que su tipo de valor es asignable. Todos los C ++ estándar contenedores biblioteca tienen este requisito.

const no significa "este valor sólo se puede cambiar bajo circunstancias especiales." Por el contrario, los medios const "Nada de lo que se les permite ver con ello hará que el cambio de ninguna manera (que se podía observar)"

Si usted tiene una variable const cualificado, no se le permite, por mandato del compilador (y su propia elección para calificarlo con const en el primer lugar), a hacer nada que pueda hacer que el cambio. Eso es lo que hace const. Podría cambiar a pesar de estás muy acciones, si es una referencia constante a un objeto no constante, o por cualquiera de una variedad de otras razones. Si el programador como saber que el referant en realidad no es constante, puede desecharla con const_cast y el cambio de eso.

Sin embargo, en su caso, una variable miembro constante, esto no es posible. La variable const cualificado no puede ser una referencia constante a no constante, porque no es una referencia en absoluto.

Editar: para un emocionante ejemplo de lo que se trata todo esto y por qué debe comportarse con respecto a la corrección const, permite echar un vistazo a lo que realmente hace un verdadero compilador. Considere este programa corto:

int main() {
  const int i = 42; 
  const_cast<int&>(i) = 0; 
  return i;
}

Y esto es lo que LLVM-G emite ++:

; ModuleID = '/tmp/webcompile/_2418_0.bc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-linux-gnu"

define i32 @main() nounwind {
entry:
  %retval = alloca i32                            ; <i32*> [#uses=2]
  %0 = alloca i32                                 ; <i32*> [#uses=2]
  %i = alloca i32                                 ; <i32*> [#uses=2]
  %"alloca point" = bitcast i32 0 to i32          ; <i32> [#uses=0]
  store i32 42, i32* %i, align 4
  store i32 0, i32* %i, align 4
  store i32 42, i32* %0, align 4
  %1 = load i32* %0, align 4                      ; <i32> [#uses=1]
  store i32 %1, i32* %retval, align 4
  br label %return

return:                                           ; preds = %entry
  %retval2 = load i32* %retval                    ; <i32> [#uses=1]
  ret i32 %retval2
}

De particular interés es la store i32 0, i32* %i, align 4 línea. Esto indica que el const_cast tuvo éxito, que en realidad asignado un cero sobre el valor que había sido inicializado a.

y modificaciones a los cualificados const puede no resultar en un cambio observable. Por lo tanto GCC produce una cadena bastante larga de poner 42 en% 0 y, a continuación loating que el 42% en 1, a continuación, almacenarlo de nuevo en% retval, y luego cargarlo en% retval2. Por lo tanto, G ++ tendrá este código satisfacer ambos requisitos, la const fue desechado, pero no hubo ningún cambio observable a i, principal vuelve 42.


Si necesita un valor que se puede cambiar, por ejemplo, en los elementos de un contenedor estándar, entonces no es necesario const.

Considere el uso de los miembros private: con captador pública y métodos setter privadas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top