2011-01-25 20 views
10

La historia comienza con algo que creí muy simple:Contenedor inmutable con contenido mutable

Necesito diseñar una clase que use algunos contenedores STL. Necesito dar acceso a los usuarios de la clase a una versión inmutable de esos contenedores. No quiero que los usuarios puedan cambiar el contenedor (no pueden push_back() en una lista, por ejemplo), pero quiero que los usuarios puedan cambiar los objetos contenidos (obtenga un elemento con back() y modifíquelo) :

class Foo 
{ 
    public: 

    // [...] 

    ImmutableListWithMutableElementsType getImmutableListWithMutableElements(); 

    // [...] 
}; 

// [...] 

myList = foo.getImmutableListWithMutableElements(); 
myElement = myList.back(); 
myElement.change(42); // OK 

// [...] 

// myList.push_back(myOtherElement); // Not possible 

a primera vista, parece que un contenedor const va a hacer. Pero, por supuesto, sólo se puede utilizar un iterador const en un recipiente const y no se puede cambiar el contenido.

en una segunda mirada, cosas como especializada de contenedores o iterador vienen a la mente. Probablemente termine con eso.

Entonces, mi pensamiento es "Alguien tiene que haber hecho ya!" o "¡Debe existir una solución elegante y genérica!" y estoy aquí haciendo mi primera pregunta sobre SO:

¿Cómo se diseña/transformar un contenedor estándar en un recipiente con el contenido inmutable mutable?

estoy trabajando en ello, pero me siento como si alguien le acaba de decir "Hey, yo que cada vez, es fácil, mira!", Por lo que pido ...

Gracias por cualquier insinuación, sugerencias o maravillosas formas genéricas para hacer eso :)


EDIT:

Después de algunos experimentos, que terminó con contenedores estándar que manejan algunos punteros inteligentes decoradas específicamente. Está cerca de la respuesta de Nikolai.

La idea de un contenedor de elementos inmutables mutables no es un concepto matar, ver las notas interesantes en respuesta Oli.

La idea de un iterador específica es correcto, por supuesto, pero no parece práctico, ya que necesito para adaptarse a cualquier tipo de contenedor.

Gracias a todos por su ayuda.

Respuesta

5

La opción más sencilla sería probablemente un contenedor STL estándar de punteros, ya que const -ness no se propaga a los objetos reales. Un problema con esto es que STL no limpia la memoria de montón que asignó. Para eso echa un vistazo a Boost Pointer Container Library o smart pointers.

+0

Sí, pero para obtener un contenedor inmutable, tengo que devolver un contenedor de const, y luego obtendré elementos de const de él, ¿no? ¿Sugiere usar const_cast? Por supuesto que es una posibilidad, pero será un dolor usar ... ¿o me perdí algo? – neuro

+0

Los punteros en el contenedor 'const' serán' const', pero no los objetos a los que apuntan. –

+0

sí, tienes razón, por supuesto. Lo único es que no es tan genérico como pensé que podría ser. Como parte de un marco, me gustaría dar la posibilidad a los desarrolladores de framework de usar cualquier tipo de contenedor. Pero usar un contenedor de puntero o un contenedor de puntero inteligente debería hacer el trabajo ... – neuro

5

En lugar de proporcionarle al usuario todo el contenedor, ¿podría simplemente proporcionarles iteradores no const para comenzar y finalizar? Esa es la forma de STL.

+1

Esto funciona, dependiendo de la definición de "modificación del contenedor". ¿'Std :: remove' cuenta como una modificación del contenedor? (Creo que sí, pero tal vez el OP está de acuerdo con eso.) –

+0

sí, pero como dijo James, con un iterador estándar no const, el usuario puede modificar el contenedor. En cualquier caso, tengo que especializar el contenedor o el iterador. Intento imaginar alguna solución que pueda "decorar" mi contenedor/iterador para tener que escribir menos código :) Pero tienes razón al decir que es STL. Un iterador especializado probablemente hará el trabajo. – neuro

+0

@James, @neuro: ¿Sería justo decir que 'std :: remove' etc. en realidad no modifica el contenedor, per se. Simplemente intercambian el contenido de varios elementos, que es algo que el usuario podría hacer incluso si 'std :: remove' no fuera permisible. –

1

La solución dolorosa:

/* YOU HAVE NOT SEEN THIS */ 
struct mutable_int { 
    mutable_int(int v = 0) : v(v) { } 
    operator int(void) const { return v; } 
    mutable_int const &operator=(int nv) const { v = nv; return *this; } 

    mutable int v; 
}; 

Discúlpame mientras tengo que castigarme a mí mismo para expiar mis pecados.

+0

Err, eso es probablemente demasiado inteligente para mí :) Gracias por el esfuerzo de todos modos;) – neuro

+0

Gracias de nuevo y buena suerte con su castigo;) – neuro

+0

Hola, soy del futuro y estoy tratando de aprender C++. ¿Alguien puede explicar lo que está pasando aquí ...? : | –

3

Necesita un iterador de estructura de datos personalizado, un contenedor alrededor de su lista privada.

template<typename T> 
class inmutable_list_it { 
public: 
    inmutable_list_it(std::list<T>* real_list) : real_list_(real_list) {} 

    T first() { return *(real_list_->begin()); } 

    // Reset Iteration 
    void reset() { it_ = real_list_->begin(); } 

    // Returns current item 
    T current() { return *it_; } 

    // Returns true if the iterator has a next element. 
    bool hasNext(); 

private: 
    std::list<T>* real_list_; 
    std::list<T>::iterator it_; 
}; 
+0

Sí, estaba pensando en algo así. Lo que me molesta es que necesito codificar una versión especializada para cualquier tipo de contenedor que quiera administrar ... Intentaré con una versión genérica de lo que propones, si mis habilidades de plantilla deficientes encajan en el desafío :) ¡Gracias por tu ayuda! – neuro

+0

gracias por sus ideas, me recuerda a algunos iteradores de Java por cierto :) – neuro