2010-05-26 10 views
8

Dada una clase como esta:El uso de una clase con miembros de datos const en un vector

class Foo 
{ 
    const int a; 
}; 

¿Es posible poner esa clase en un vector? Cuando lo intento, mi compilador me dice que no puede usar el operador de asignación predeterminado. Intento escribir el mío, pero buscar en Google me dice que es imposible escribir un operador de asignación para una clase con miembros de datos const. Una publicación que encontré decía que "si creaste [el miembro de datos] const significa que no quieres que la asignación suceda en primer lugar". Esto tiene sentido. He escrito una clase con miembros de datos de const, y nunca tuve la intención de usar asignaciones en ella, pero aparentemente necesito una asignación para ponerla en un vector. ¿Hay alguna forma de evitar esto que aún preserve la corrección de const?

+0

¿Por qué exactamente necesita un miembro de const? ¿Qué es 'Foo' realmente? – fredoverflow

Respuesta

3

Utilice un vector de punteros std::vector<Foo *>. Si desea evitar la molestia de limpiarse después de usted, use boost::ptr_vector.

+0

Muy bien, gracias. Voy a intentar esto. – Max

1

Editar: Mi puñalada inicial durante mi descanso para tomar café, static const int a; no funcionará para el caso de uso de la OP tiene en cuenta que los comentarios iniciales confirman, por lo que estoy reescribiendo y expandir mi respuesta.

La mayoría de las veces, cuando quiero hacer constante un elemento de una clase, es una constante cuyo valor es constante para todos los instantes de la clase. En ese caso, yo uso una variable static const:

class Foo 
{ 
public: 
    static const int a; 
}; 

Esos no necesitan ser copiados entre los casos, por lo que si se aplica, eso sería arreglar su problema de asignación. Desafortunadamente, el OP ha indicado que esto no funcionará para el caso que el OP tiene en mente.

Si desea crear un sólo lectura valor que los clientes no pueden modificar, puede que sea una variable miembro privada y sólo exponerlo a través de un método de obtención const, como otro post en este hilo indica:

class Foo 
{ 
public: 
    int get_a() const { return a; } 
private: 
    int a; 
}; 

La diferencia entre esto y

es:

  • El const int le garantiza que ni siquiera la implementación de la clase podrá acumular el valor de a durante la vida útil del objeto. Esto significa que la asignación legítimamente no funcionará, ya que eso estaría intentando modificar el valor de a después de que el objeto haya sido creado. (Por eso, por cierto, escribir un operator=() personalizado que omita la copia de a es probablemente una mala idea en cuanto al diseño.)
  • El acceso es diferente – tiene que pasar por un getter en lugar de acceder al miembro directamente.

En la práctica, al elegir entre los dos, utilizo los miembros de solo lectura. Hacerlo probablemente significa que podrá reemplazar el valor de un objeto con el valor de otro objeto sin violar la semántica en absoluto. Veamos cómo funcionaría en tu caso.

Considere su objeto Grid, con un ancho y alto. Cuando cree inicialmente el vector, y supongamos que reserva un espacio inicial con vector::reserve(), su vector se rellenará con grillas iniciales inicializadas por defecto (es decir, vacías).Cuando va a asignar una posición particular en el vector, o empuja una cuadrícula al final del vector, reemplaza el valor del objeto en esa posición con una cuadrícula que tiene cosas reales. ¡Pero puede estar bien con esto! Si la razón por la que desea que el ancho y la altura sean constantes es garantizar la consistencia entre ancho y alto y el resto del contenido de su objeto Cuadrícula, y ha verificado que no importa si se reemplazan el ancho y la altura antes o después de que se reemplacen otros elementos de Grid, esta asignación debería ser segura porque al final de la tarea, se habrá reemplazado todo el contenido de la instancia y volverá a estar en un estado consistente. (Si la falta de atomicidad de la asignación por defecto era un problema, que probablemente se podría evitar esto mediante la implementación de su propio operador de asignación, que utiliza un constructor de copia y una operación de swap().)

En resumen, lo que ganas por usar captadores de solo lectura es la capacidad de usar los objetos en un vector o cualquier contenedor con semántica de valores. Sin embargo, le corresponde a usted asegurarse de que ninguna de las operaciones internas de Grid (o las operaciones de los amigos de Grid) violen esta coherencia, porque el compilador no bloqueará el ancho y la altura para usted. Esto se aplica a la construcción predeterminada, la construcción de copias y la asignación también.

+1

Hacer una variable estática hace que todas las instancias de esa clase la compartan. Si realmente necesita que la constante sea única para las instancias, esto definitivamente no funcionará en absoluto. –

+0

Hm, podría haber entendido mal el caso de uso. ¿Es esta una constante que necesita variar de una instancia a otra? Todavía no me he encontrado con el caso en el que realmente necesitaba uno de esos. –

+1

Lo es. En el programa que estoy escribiendo, tengo una clase Grid, que tiene variables de ancho y alto.Nunca quiero que el ancho y la altura cambien una vez que se hayan inicializado, por lo que los hago constantes, pero sí quiero grillas de varios tamaños en un vector. – Max

7

He escrito una clase con miembros de datos const, y nunca tuve la intención de usar asignaciones en ella, pero aparentemente necesito una asignación para ponerlo en un vector. ¿Hay alguna forma de evitar esto que aún preserve la corrección de const?

Hay que preguntarse si la siguiente restricción aún mantiene

a = b; 
/* a is now equivalent to b */ 

Si esta restricción no es cierto para a y b ser del tipo Foo (hay que definir la semántica de lo que significa "equivalentes" !), entonces simplemente no puede poner Foo en un contenedor estándar. Por ejemplo, auto_ptr no se puede poner en contenedores estándar porque infringe ese requisito.

Si puede decir acerca de su tipo que satisface esta restricción (por ejemplo, si el miembro const no participa de ninguna manera en el valor de su objeto, pero luego considere convertirlo en un miembro de datos estáticos), entonces puede escribir su propio operador de asignación

class Foo 
{ 
    const int a; 
public: 
    Foo &operator=(Foo const& f) { 
    /* don't assign to "a" */ 
    return *this; 
    } 
}; 

Pero piense dos veces!. Para mí, parece que su tipo no cumple la restricción!

+0

Entonces, ¿está diciendo que si estoy seguro de que nunca escribiré 'a = b' con mi tipo, entonces está bien escribir un operador de asignación que no haga nada? Tiene sentido para mí, pero ¿qué te hace pensar que mi tipo no satisface esa restricción? -EDIT- Pensándolo bien, veo lo que obtienes en mejor. Estoy de acuerdo en que mi tipo no cumple con la restricción. – Max

+1

@Max: Eso no es lo que dice. Él dice que haga toda la tarea como siempre, pero ignore la asignación del miembro 'const'. Y el contenedor es libre de hacer 'a = b', no tiene nada que ver con lo que harás con él. – GManNickG

0

Estoy considerando hacer que el miembro de datos no constante, pero privado y sólo se puede acceder por una función get, así:

class Foo 
{ 
    private: 
     int a; 
    public: 
     int getA() const {return a;} 
}; 

¿Es esta 'tan bueno' como const? ¿Tiene alguna desventaja?

+0

No tenemos idea de lo que está haciendo con sus datos. pero si originalmente era 'const' para que los clientes de la clase no pudieran modificarlo, sí. Esto se conoce como encapsulación; hacer que los datos sean privados y exponerlos a través de métodos. – GManNickG

Cuestiones relacionadas