c++ - constructor copy
El operador de asignaciĆ³n predeterminado=en c++ es una copia superficial? (7)
Solo una simple pregunta rápida que no pude encontrar una respuesta sólida a ningún otro lugar. ¿El operador predeterminado es solo una copia superficial de todos los miembros de la clase en el lado derecho?
Class foo {
public:
int a, b, c;
};
foo f1, f2;
...
f1 = f2;
sería idéntico a:
f1.a = f2.a;
f1.b = f2.b;
f1.c = f2.c;
Esto parece ser cierto cuando lo pruebo, pero debo estar seguro de que no me estoy perdiendo ningún caso específico.
Como se ilustra en el fragmento de código a continuación, el operador = (asignación) para STL realiza una copia profunda.
#include <iostream>
#include <stack>
#include <map>
#include <vector>
using namespace std;
int main(int argc, const char * argv[]) {
/* performs deep copy */
map <int, stack<int> > m;
stack <int> s1;
stack <int> s2;
s1.push(10);
cout<<&s1<<" "<<&(s1.top())<<" "<<s1.top()<<endl; //0x7fff5fbfe478 0x100801200 10
m.insert(make_pair(0, s1));
cout<<&m[0]<<" "<<&(m[0].top())<<" "<<m[0].top()<<endl; //0x100104248 0x100803200 10
m[0].top() = 1;
cout<<&m[0]<<" "<<&(m[0].top())<<" "<<m[0].top()<<endl; //0x100104248 0x100803200 1
s2 = m[0];
cout<<&s2<<" "<<&(s2.top())<<" "<<s2.top()<<endl; //0x7fff5fbfe448 0x100804200 1
s2.top() = 5;
cout<<&s2<<" "<<&(s2.top())<<" "<<s2.top()<<endl; //0x7fff5fbfe448 0x100804200 5
cout<<&m[0]<<" "<<&(m[0].top())<<" "<<m[0].top()<<endl; //0x100104248 0x100803200 1
cout<<endl<<endl;
map <int, stack<int*> > mp;
stack <int*> s1p;
stack <int*> s2p;
s1p.push(new int);
cout<<&s1p<<" "<<&(s1p.top())<<" "<<s1p.top()<<endl; //0x7fff5fbfe360 0x100805200 0x100104290
mp.insert(make_pair(0, s1p));
cout<<&mp[0]<<" "<<&(mp[0].top())<<" "<<mp[0].top()<<endl; //0x1001042e8 0x100806200 0x100104290
mp[0].top() = new int;
cout<<&mp[0]<<" "<<&(mp[0].top())<<" "<<mp[0].top()<<endl; //0x1001042e8 0x100806200 0x100104320
s2p = mp[0];
cout<<&s2p<<" "<<&(s2p.top())<<" "<<s2p.top()<<endl; //0x7fff5fbfe330 0x100807200 0x100104320
s2p.top() = new int;
cout<<&s2p<<" "<<&(s2p.top())<<" "<<s2p.top()<<endl; //0x7fff5fbfe330 0x100807200 0x100104340
cout<<&mp[0]<<" "<<&(mp[0].top())<<" "<<mp[0].top()<<endl; //0x1001042e8 0x100806200 0x100104320
cout<<endl<<endl;
vector<int> v1,v2;
vector<int*> v1p, v2p;
v1.push_back(1);
cout<<&v1<<" "<<&v1[0]<<" "<<v1[0]<<endl; //0x7fff5fbfe290 0x100104350 1
v2 = v1;
cout<<&v2<<" "<<&v2[0]<<" "<<v2[0]<<endl; //0x7fff5fbfe278 0x100104360 1
v2[0] = 10;
cout<<&v2<<" "<<&v2[0]<<" "<<v2[0]<<endl; //0x7fff5fbfe278 0x100104360 10
cout<<&v1<<" "<<&v1[0]<<" "<<v1[0]<<endl; //0x7fff5fbfe290 0x100104350 1
cout<<endl<<endl;
v1p.push_back(new int);
cout<<&v1p<<" "<<&v1p[0]<<" "<<v1p[0]<<endl; //0x7fff5fbfe260 0x100104380 0x100104370
v2p = v1p;
cout<<&v2p<<" "<<&v2p[0]<<" "<<v2p[0]<<endl; //0x7fff5fbfe248 0x100104390 0x100104370
v2p[0] = new int;
cout<<&v2p<<" "<<&v2p[0]<<" "<<v2p[0]<<endl; //0x7fff5fbfe248 0x100104390 0x1001043a0
cout<<&v1p<<" "<<&v1p[0]<<" "<<v1p[0]<<endl; //0x7fff5fbfe260 0x100104380 0x100104370
return 0;
}
La copia "superficial" versus la copia "profunda" es menos significativa en C ++ que en C o Java.
Para ilustrar esto, he cambiado tu clase de Foo
de tres int
s a int
, a int*
y a vector<int>
:
#include <iostream>
#include <vector>
class Foo {
public:
int a;
int *b;
std::vector<int> c;
};
using namespace std;
int main() {
Foo f1, f2;
f1.a = 42;
f1.b = new int(42);
f1.c.push_back(42);
f2 = f1;
cout << "f1.b: " << f1.b << " &f1.c[0]: " << &f1.c[0] << endl;
cout << "f2.b: " << f2.b << " &f2.c[0]: " << &f2.c[0] << endl;
}
Cuando este programa se ejecuta, produce el siguiente resultado:
f1.b: 0x100100080 &f1.c[0]: 0x100100090
f2.b: 0x100100080 &f2.c[0]: 0x1001000a0
El int
es aburrido, así que lo he dejado fuera. Pero observe la diferencia entre int*
y el vector<int>
: int*
es el mismo en f1 y f2; es lo que podríamos llamar una "copia superficial". Sin embargo, el vector<int>
es diferente entre f1 y f2; Es lo que podríamos llamar una "copia profunda".
Lo que realmente sucedió aquí es que el operator =
predeterminado operator =
en C ++ se comporta como si el operator =
para todos sus miembros se llamara en orden. El operator =
para int
s, int*
s, y otros tipos primitivos es solo una copia superficial de bytes. El operator =
para el vector<T>
realiza una copia profunda.
Así que diría que la respuesta a la pregunta es: No, el operador de asignación predeterminado en C ++ no realiza una copia superficial. Pero tampoco realiza una copia profunda. El operador de asignación predeterminado en C ++ aplica recursivamente los operadores de asignación de los miembros de la clase.
No. operator=
no realiza una copia en absoluto. Es un operador de asignación , no operador de copia .
El operador de asignación predeterminado asigna a cada miembro.
Sí, el operator=
predeterminado operator=
es una copia superficial.
Por cierto, la diferencia real entre shallow copy
y deep copy
hace visible cuando la clase tiene punteros como campos miembros. En ausencia de punteros, no hay diferencia (a mi entender).
Para conocer la diferencia entre ellos, vea estos temas (en el propio ):
Sí, solo copia el objeto de forma inteligente, lo que puede causar problemas para los punteros sin procesar.
Si a, byc fueran clases, se llamaría al operador de asignación para esas clases, por lo que el compilador no se limita a copiar los contenidos en bruto de la memoria, sino que, como se ha indicado en otros, se copiarán todos los punteros en bruto sin ningún intento de duplicar apuntado a la cosa, lo que le da el potencial para colgar punteros.
Yo diría que el operator=
defecto operator=
es una copia . Copia cada miembro.
La distinción entre una copia superficial y una copia profunda no surge a menos que los miembros que se copien sean algún tipo de indirección, como un puntero. En lo que respecta al operator=
predeterminado operator=
, depende del miembro que se copie lo que significa "copiar", podría ser profundo o superficial.
Específicamente, sin embargo, copiar un puntero en bruto simplemente copia el valor del puntero, no hace nada con el referendo. Por lo tanto, los objetos que contienen miembros punteros son copiados de forma superficial por el operator=
predeterminado operator=
.
Existen varios esfuerzos para escribir punteros inteligentes que realizan operaciones de clonación en la copia, por lo que si los usa en todas partes en lugar de punteros sin procesar, el operator=
predeterminado operator=
realizará una copia profunda.
Si su objeto tiene contenedores estándar como miembros, puede ser confuso para (por ejemplo) un programador de Java decir que operator=
es una "copia superficial". En Java, un miembro de Vector
es realmente solo una referencia, por lo que "copia superficial" significa que los miembros de Vector
no están clonados: origen y destino se refieren al mismo objeto de vector subyacente. En C ++, se copiará un miembro vector
, junto con su contenido, ya que el miembro es un objeto real, no una referencia (y vector::operator=
garantiza que los contenidos se copien con él).
Si su miembro de datos es un vector de punteros, entonces no tiene una copia profunda o una copia superficial. Tiene una copia semi profunda, donde los objetos de origen y destino tienen vectores separados, pero los elementos vectoriales correspondientes de cada punto fijo apuntan al mismo objeto no clonado.