2010-11-29 13 views
9

Estoy un poco confundido en este momento con respecto a la semántica de referencia de C++. Supongamos que tengo una clase que devuelve una referencia constante:Impedir la copia al inicializar un objeto no const desde una referencia de referencia

class foo 
{ 
private: 
    std::map<int, int> stuff; 
public: 
    const std::map<int, int>& getStuff() 
    { 
     return stuff; 
    } 
}; 

y lo uso de la siguiente manera:

foo f; 
const std::map<int, int>& s = f.getStuff(); 

lo cual está bien, pero si tuviera que utilizarlo como sigue:

foo f; 
std::map<int, int> s = f.getStuff(); 

¿Qué sucede exactamente?

Si entiendo correctamente, se devolvió una referencia constante a stuff y se creó una copia en s en la que puedo causar estragos. ¿Hubiera alguna forma de evitar esto?

edición:

Así que no hay manera de evitar el constructor de copia que se llama aquí, por std :: mapa de todos modos ...

+0

AFAIK se llamará al constructor de copias y no veo una manera de evitar esto. – Lagerbaer

+1

Tienes razón. Pero siempre puedes hacer 's' const si quieres evitar eso. Además, hacer 's' a const ref significa que debes asegurarte de que' f' permanezca activa mientras tanto, lo que sucederá automáticamente si es una variable automática previamente declarada como lo es aquí, pero requiere administración si fue creado en el montón. –

+2

Pero cualquier modificación que realice en 's' no afectará al objeto original' cosas '. Entonces, ¿cómo te afecta? – Naveen

Respuesta

5

Respuesta corta: no, no puede evitarlo. El cliente no puede modificar ify el original, pero si le das al cliente acceso de lectura al mapa, entonces el cliente es responsable de no hacer cosas estúpidas con la información; la clase no puede evitar eso.

Respuesta larga: tal vez, pero no realmente. Si realmente desea hacer la copia difícil, puede envolver el mapa en una clase con un constructor de copia privada y un operador de asignación. De esta forma, la asignación s será ilegal (rechazada por el compilador). El cliente aún podrá leer los elementos del mapa por partes y rellenar un nuevo mapa con ellos, una copia manual, pero la única manera de evitar eso es restringir el acceso de lectura en la clase contenedora, que tipo de derrota el propósito de getStuff.

+0

A Wrapper! Sí, eso es lo que necesitaba, gracias !!! mi cerebro está totalmente frito esta noche : s –

4
std::map<int, int> s = f.getStuff(); 

Esto invoca el constructor copia std::map<int, int> y hace una copia del objeto. El contenido del mapa stuff se copia en el nuevo mapa s.

No puede causar estragos con el objeto original porque s es un objeto nuevo sin relación alguna con el objeto original, aparte del hecho de que el objeto original y el nuevo tienen los mismos contenidos.

Es imposible causar estragos legítimos con el mapa stuff a través de la referencia const devuelta por foo::getStuff(). La única forma de modificar el mapa sería a través de const_cast y modificar un objeto mediante un puntero o referencia obtenida a través de const_cast puede generar un comportamiento indefinido.

+0

Increíble que tantos votos, a pesar del hecho de que no responde a la pregunta '¿Habría alguna forma de evitar esto?' – Chubsdad

+0

Justo lo suficiente, supongo que es utilizar la referencia o ir 'const std :: map s = f.getStuff();' luego –

+1

@Chubsdad: No, estaba abordando la meta-pregunta del OP de "¿Cómo funciona esta línea? de código realmente funciona?"Una vez que se da cuenta de que el objeto original se ha copiado en su totalidad y que el nuevo objeto no tiene ninguna relación con el objeto original, además de tener el mismo contenido, debe ser sencillo. –

1

Sí, su entendimiento es el correcto. Esto no es nada por inicialización de copia e implica el uso de un constructor de copia. No hay forma de evitar esta copia, ya que esto es lo que solicita el fragmento de código que ha mostrado.

Incluso si causa estragos en la copia, no se preocupe. El original aún está a salvo. La preocupación es solo si el proceso de creación de una copia puede causar estragos, pero ese es un problema diferente.

C++ 03 referencias relevantes:

$ 8.5/12- "La inicialización que se produce en el paso de argumentos, la función vuelta, lanzando una excepción (15.1), manejo de una excepción (15.3), y las listas de inicializadores incluidos en el bloque (8.5.1) se llama copia-inicialización y es equivalente a la forma T x = a; "

$ 8,5/14- "Si la inicialización es directo de inicialización, o si es copia de inicialización en la versión cv-calificado del tipo de fuente de es la misma clase que, o una clase derivada de , la clase del destino , los constructores son considerados.Los constructores aplicables se enumeran (13.3.1.3), y el mejor se elige a través de la resolución de sobrecarga (13.3). El constructor así seleccionado se llama para inicializar el objeto, con la (s) expresión (es) de inicializador como su argumento (s) . Si ningún constructor se aplica, o la resolución de sobrecarga es ambigua, la inicialización es mal formados "

+2

Por lo tanto, si el OP está un poco confundido con respecto a la semántica de referencia, citar el estándar es probablemente un poco autoritario ... –

0

Sí, esto creará una copia del mapa

cuanto a su pregunta -.. Depende solo cuánto desea evitar. En general, si esta es una clase que usted mismo declaró, puede hacer que el constructor de copia o operator = sea privado para evitar que se use, pero obviamente, esto le prohibirá algunas cosas.

Cuestiones relacionadas