2010-02-06 31 views
15

Estoy un poco confundido en cuanto a la mecánica del constructor de copias. Corrija si estoy equivocado:¿Puedo llamar a un constructor de copia de forma explícita?

Si un método toma una referencia a un objeto como parámetro, y la clase define un copiador, entonces la clase usa el constructor para crear una copia de sí mismo y se pasa a la función en lugar de una referencia al objeto original?

Por otra parte, uno puede llamar

Object * obj = new Object(&anotherObject); 

para crear una copia de otroObjeto?

+1

Su terminología no debe sugerir que 'anotherObject' está creando una copia de sí mismo .. el objeto que se crea después de que el nuevo será la creación de una copia de' otroObjeto'. –

+5

@Hassan: No cambie el significado de la pregunta sin consultar el OP. Esa es una distinción importante. '& anotherObject' es un puntero, no una referencia. –

+0

"no se puede decir" que 'new object (& anotherObject)' creará una copia de otro objeto implícitamente (que es lo que sugiere la fraseología). Para que esa afirmación sea verdadera, la sintaxis necesita ser ajustada. –

Respuesta

26

No, si una función de tomar una referencia:

void f1(Object & o); // call by reference 

entonces no se hace copia. Si una función toma un valor:

void f2(Object o); // call by value 

, el compilador crea una copia utilizando el constructor de copia.

Y sí, cuando se dice:

Object * obj = new Object(anotherObject); // not &anotherObject 

el constructor de copia se utiliza de forma explícita (suponiendo otroObjeto es de tipo Object.) No hay nada mágico sobre el uso de new aquí, sin embargo - en este caso:

Object obj2(anotherObject); 

el constructor de copia también se utiliza.

+1

"entonces el compilador crea una copia utilizando el constructor de copia". - Puede usar el move-constructor, y también este es un escenario de copia-elisión así que tal vez incluso ningún constructor en absoluto. –

7

Si un método toma una referencia a un objeto como un parámetro, no se llamará al constructor de copia. Si ese fuera el caso, una llamada al propio constructor de la copia habría resultado en un ciclo infinito (ya que toma una referencia como argumento).

Esa línea no es una forma válida de llamar a un constructor de copia. Espera una referencia como argumento, no como un puntero.

+0

En realidad, el argumento copiar-constructor no es sólido. El estándar de lenguaje permite la copia opcional en algunos casos y señala explícitamente que la implementación debe asegurarse de que no haya recursión infinita en caso de copia-constructor, como un ejemplo (vea la nota al pie 93 en C++ 03) – AnT

+0

@AndreyT: Interesante. De todos modos, eso no cambia las cosas en este caso, ya que estamos hablando de * no * copiar en caso de referencia, lo cual es bastante seguro. –

+0

Bueno, la partición del estándar al que me refería se refiere en realidad a la inicialización de referencia, y permite copiar explícitamente en algunas situaciones (en el caso de la inicialización const-reference). Por supuesto, los compiladores normalmente no participan en copias descabelladas e injustificadas, pero se conocen ejemplos de compiladores que crean extrañas copias ilógicas (aún legales). – AnT

1

No en ambos casos. En el primer caso, la referencia a ese objeto se pasa y la copia no se crea. En el segundo caso, está pasando un puntero al constructor de object, por lo que no se crea ninguna copia. Así objeto debe tener un constructor (no es un constructor de copia), que es algo así como object(anotherClass*)

1

constructor de copia se llama sólo cuando pasa por valor, no por referencia . Por referencia, no se necesita copiar (¡esto es parte de para qué sirven las referencias!), Por lo que no se llama a ningún constructor de copia.

3

El hecho de que esté realizando una llamada a un método no tiene importancia aquí. La inicialización del parámetro de referencia durante una llamada a la función no es diferente de una inicialización de referencia independiente y se rige por las mismas reglas.

Las reglas de inicialización de referencia son un poco complicadas, pero la conclusión es que si el inicializador es un valor l (el argumento en la llamada al método en su caso) y el tipo de referencia es el mismo que el tipo de el inicializador (es decir, el tipo de parámetro es el mismo que el tipo de argumento), entonces la referencia se vinculará directamente. Es decir. no se crea copia

Object a; // class type 
Object &r = a; // no copying 
const Object &cr = a; // no copying 

Si estos requisitos no se cumplen (por ejemplo, si el inicializador es un valor r), entonces todo depende. En algunos casos, la copia podría y tendrá lugar. Por ejemplo

const Object &tr = Object(); 

puede ser interpretado por el compilador como

const Object &tr = Object(Object(Object(Object()))); 

con número finito dependiente de la implementación de copyings. Por supuesto, por razones de eficiencia, los compiladores normalmente intentan no crear copias innecesarias, incluso cuando pueden copiar.

Un ejemplo clásico que a menudo suscita debate sobre la validez del comportamiento copia del compilador es la inicialización de referencia en expresiones como la siguiente

Object a; 
const Object &r = <some condition> ? a : Object(); 

Una persona familiarizada con la semántica de C++ de referencia entendería que expresiones como lo anterior es probablemente la razón detrás del permiso estándar para realizar copias superfluas durante la inicialización de referencia.

+0

¿Estás seguro de esa cosa de profundidad infinita? C++ 03 8.5.3 dice que solo hay dos casos: (a) "La referencia está ligada al objeto representado por el valor r (ver 3.10) oa un subobjeto dentro de ese objeto", y (b) " Se crea un temporal de tipo "cv1 T2" [sic] y se llama a un constructor para copiar todo el objeto rvalue en el temporal. La referencia está vinculada al temporal o a un subobjeto dentro del temporal ". Tenga en cuenta que el caso (b) dice * obligado a *, no * inicializado desde *, por lo que no veo cómo eso (b) permite más que el extra temporal. –

+0

Por supuesto, C++ 14 (que salió después de que se escribió su respuesta) solo permite el caso (a), es decir, no se pueden crear más temporales más que 'Objeto()'. No revisé la redacción de C++ 11 aunque ISTR tenía un defecto con respecto a la inicialización de referencia de los valores de r. –

0

sí mediante la colocación de nuevo de esta manera:

Object dstObject; 
new(&dstObject) Object(&anotherObject); 
Cuestiones relacionadas