Alguien lo mencionó en el IRC como el problema del corte.
"Rebanar" es donde se asigna un objeto de una clase derivada a una instancia de una clase base, perdiendo así parte de la información; parte de ella se "corta".
Por ejemplo,
class A {
int foo;
};
class B : public A {
int bar;
};
Entonces, un objeto de tipo B
tiene dos miembros de datos foo
y bar
.
Entonces, si tuvieras que escribir esto:
B b;
A a = b;
Entonces la información b
sobre el miembro bar
se pierde en a
.
La mayoría de las respuestas aquí no explican cuál es el problema real con el corte. Solo explican los casos benignos de rebanar, no los traicioneros. Suponga, al igual que las otras respuestas, que está tratando con dos clases A
y B
, de dónde B
deriva (públicamente) A
.
En esta situación, C ++ le permite pasar una instancia del operador de asignación de B
to A
(y también al constructor de copia). Esto funciona porque una instancia de B
se puede convertir en a const A&
, que es lo que los operadores de asignación y los constructores de copia esperan que sean sus argumentos.
B b;
A a = b;
No sucede nada malo allí: solicitó una instancia de la A
cual es una copia B
, y eso es exactamente lo que obtiene. Seguro, a
no contendrá algunos de b
los miembros, pero ¿cómo debería hacerlo? Es una A
, después de todo, no una B
, por lo que ni siquiera ha oído acerca de estos miembros, y mucho menos sería capaz de almacenarlos.
B b1;
B b2;
A& a_ref = b2;
a_ref = b1;
//b2 now contains a mixture of b1 and b2!
Podrías pensar que b2
será una copia de b1
después. Pero, por desgracia, es no ! Si lo inspecciona, descubrirá que b2
es una criatura de Frankenstein, hecha de algunos trozos de b1
(los trozos de los que B
hereda A
) y algunos trozos de b2
(los trozos que solo B
contiene). ¡Ay!
¿Qué sucedió? Bueno, C ++ por defecto no trata a los operadores de asignación como virtual
. Por lo tanto, la línea a_ref = b1
llamará al operador de asignación de A
, no al de B
. Esto se debe a que, para las funciones no virtuales, el tipo declarado (formalmente: estático ) (que es A&
) determina qué función se llama, a diferencia del tipo real (formalmente: dinámico ) (que sería B
, ya que hace a_ref
referencia a una instancia de B
) . Ahora, A
el operador de asignación de, obviamente, solo conoce los miembros declarados A
, por lo que copiará solo esos, dejando los miembros agregados B
sin cambios.
La asignación solo a partes de un objeto generalmente tiene poco sentido, pero C ++, desafortunadamente, no proporciona una forma incorporada de prohibir esto. Sin embargo, puede enrollar el suyo. El primer paso es hacer que el operador de asignación sea virtual . Esto garantizará que siempre se llame al operador de asignación del tipo real , no al tipo declarado . El segundo paso es utilizar dynamic_cast
para verificar que el objeto asignado tiene un tipo compatible. El tercer paso es hacer la asignación real en un miembro (¡protegido!) assign()
, Ya que B
's assign()
probablemente querrá usar A
' s assign()
para copiar A
's, miembros.
class A {
public:
virtual A& operator= (const A& a) {
assign(a);
return *this;
}
protected:
void assign(const A& a) {
// copy members of A from a to this
}
};
class B : public A {
public:
virtual B& operator= (const A& a) {
if (const B* b = dynamic_cast<const B*>(&a))
assign(*b);
else
throw bad_assignment();
return *this;
}
protected:
void assign(const B& b) {
A::assign(b); // Let A's assign() copy members of A from b to this
// copy members of B from b to this
}
};
Tenga en cuenta que, por pura conveniencia, B
's operator=
anula covariablemente el tipo de retorno, ya que sabe que está devolviendo una instancia de B
.
Si tiene una clase base A
y una clase derivada B
, puede hacer lo siguiente.
void wantAnA(A myA)
{
// work with myA
}
B derived;
// work with the object "derived"
wantAnA(derived);
Ahora el método wantAnA
necesita una copia de derived
. Sin embargo, el objeto derived
no se puede copiar por completo, ya que la clase B
podría inventar variables miembro adicionales que no están en su clase base A
.
Por lo tanto, para llamar wantAnA
, el compilador "cortará" todos los miembros adicionales de la clase derivada. El resultado podría ser un objeto que no deseaba crear, porque
A
-objeto ( B
se pierde todo comportamiento especial de la clase ).Todas estas son buenas respuestas. Solo me gustaría agregar un ejemplo de ejecución al pasar objetos por valor vs por referencia:
#include <iostream>
using namespace std;
// Base class
class A {
public:
A() {}
A(const A& a) {
cout << "'A' copy constructor" << endl;
}
virtual void run() const { cout << "I am an 'A'" << endl; }
};
// Derived class
class B: public A {
public:
B():A() {}
B(const B& a):A(a) {
cout << "'B' copy constructor" << endl;
}
virtual void run() const { cout << "I am a 'B'" << endl; }
};
void g(const A & a) {
a.run();
}
void h(const A a) {
a.run();
}
int main() {
cout << "Call by reference" << endl;
g(B());
cout << endl << "Call by copy" << endl;
h(B());
}
La salida es:
Call by reference
I am a 'B'
Call by copy
'A' copy constructor
I am an 'A'
La tercera coincidencia en Google para "segmentación de C ++" me da este artículo de Wikipedia. http://en.wikipedia.org/wiki/Object_slicing y esto (acalorado, pero las primeras publicaciones definen el problema): http://bytes.com/forum/thread163565.html
Entonces es cuando asigna un objeto de una subclase a la superclase. La superclase no sabe nada de la información adicional en la subclase y no tiene espacio para almacenarla, por lo que la información adicional se "corta".
Si esos enlaces no brindan suficiente información para una "buena respuesta", edite su pregunta para hacernos saber qué más está buscando.
El problema de la segmentación es grave porque puede provocar daños en la memoria y es muy difícil garantizar que un programa no lo sufra. Para diseñarlo fuera del lenguaje, las clases que admiten la herencia deben ser accesibles solo por referencia (no por valor). El lenguaje de programación D tiene esta propiedad.
Considere la clase A y la clase B derivada de A. La corrupción de la memoria puede ocurrir si la parte A tiene un puntero p, y una instancia B que apunta p a los datos adicionales de B. Luego, cuando los datos adicionales se cortan, p apunta a basura.
En C ++, un objeto de clase derivada se puede asignar a un objeto de clase base, pero no es posible al revés.
class Base { int x, y; };
class Derived : public Base { int z, w; };
int main()
{
Derived d;
Base b = d; // Object Slicing, z and w of d are sliced off
}
La división de objetos ocurre cuando se asigna un objeto de clase derivada a un objeto de clase base, los atributos adicionales de un objeto de clase derivada se cortan para formar el objeto de clase base.
Entonces ... ¿Por qué es malo perder la información derivada? ... porque el autor de la clase derivada puede haber cambiado la representación de tal manera que cortar la información adicional cambia el valor que representa el objeto. Esto puede suceder si la clase derivada se usa para almacenar en caché una representación que es más eficiente para ciertas operaciones, pero costosa de transformar de nuevo a la representación base.
También pensé que alguien también debería mencionar lo que debe hacer para evitar cortar ... Obtenga una copia de los estándares de codificación C ++, las directrices de 101 reglas y las mejores prácticas. Lidiar con el corte es el # 54.
Sugiere un patrón algo sofisticado para abordar completamente el problema: tenga un constructor de copia protegido, un DoClone virtual puro protegido y un Clone público con una afirmación que le dirá si una clase derivada (adicional) no pudo implementar DoClone correctamente. (El método Clone hace una copia profunda adecuada del objeto polimórfico).
También puede marcar el constructor de copia en la base como explícito, lo que permite el corte explícito si lo desea.
El problema de la división en C ++ surge de la semántica de valor de sus objetos, que se mantuvo principalmente debido a la compatibilidad con las estructuras de C. Necesita usar una referencia explícita o sintaxis de puntero para lograr el comportamiento "normal" del objeto que se encuentra en la mayoría de los otros lenguajes que utilizan objetos, es decir, los objetos siempre se pasan por referencia.
La respuesta corta es que corta el objeto asignando un objeto derivado a un objeto base por valor , es decir, el objeto restante es solo una parte del objeto derivado. Para preservar la semántica del valor, la segmentación es un comportamiento razonable y tiene usos relativamente raros, que no existen en la mayoría de los otros lenguajes. Algunas personas lo consideran una característica de C ++, mientras que muchos lo consideran una de las peculiaridades / fallas de C ++.
1. LA DEFINICIÓN DE PROBLEMA DE REBANADO
Si D es una clase derivada de la clase base B, entonces puede asignar un objeto de tipo Derived a una variable (o parámetro) de tipo Base.
EJEMPLO
class Pet
{
public:
string name;
};
class Dog : public Pet
{
public:
string breed;
};
int main()
{
Dog dog;
Pet pet;
dog.name = "Tommy";
dog.breed = "Kangal Dog";
pet = dog;
cout << pet.breed; //ERROR
Aunque se permite la asignación anterior, el valor que se asigna a la variable mascota pierde su campo de raza. A esto se le llama el problema del corte .
2. CÓMO SOLUCIONAR EL PROBLEMA DEL REBANADO
Para vencer el problema, usamos punteros a variables dinámicas.
EJEMPLO
Pet *ptrP;
Dog *ptrD;
ptrD = new Dog;
ptrD->name = "Tommy";
ptrD->breed = "Kangal Dog";
ptrP = ptrD;
cout << ((Dog *)ptrP)->breed;
En este caso, no se perderá ninguno de los miembros de datos o funciones miembro de la variable dinámica a la que apunta ptrD (objeto de clase descendiente). Además, si necesita utilizar funciones, la función debe ser una función virtual.
Me parece que la división no es un problema tanto más que cuando sus propias clases y programas están mal diseñados / diseñados.
Si paso un objeto de subclase como parámetro a un método, que toma un parámetro de tipo superclase, ciertamente debería ser consciente de eso y saber internamente, el método llamado funcionará solo con el objeto de superclase (también conocido como clase base).
Me parece que solo la expectativa irrazonable de que proporcionar una subclase donde se solicita una clase base, de alguna manera daría como resultado resultados específicos de subclase, causaría que el corte sea un problema. Es un diseño deficiente en el uso del método o una implementación de subclase deficiente. Supongo que suele ser el resultado de sacrificar un buen diseño de programación orientada a objetos en favor de la conveniencia o la mejora del rendimiento.
Bien, lo intentaré después de leer muchas publicaciones que explican el corte de objetos, pero no cómo se vuelve problemático.
El escenario vicioso que puede resultar en la corrupción de la memoria es el siguiente:
Cortar significa que los datos agregados por una subclase se descartan cuando un objeto de la subclase se pasa o se devuelve por valor o desde una función que espera un objeto de clase base.
Explicación: considere la siguiente declaración de clase:
class baseclass
{
...
baseclass & operator =(const baseclass&);
baseclass(const baseclass&);
}
void function( )
{
baseclass obj1=m;
obj1=m;
}
Como las funciones de copia de la clase base no saben nada sobre el derivado, solo se copia la parte base del derivado. Esto se conoce comúnmente como rebanar.
class A
{
int x;
};
class B
{
B( ) : x(1), c('a') { }
int x;
char c;
};
int main( )
{
A a;
B b;
a = b; // b.c == 'a' is "sliced" off
return 0;
}
Veo que todas las respuestas mencionan cuando la división de objetos ocurre cuando se cortan los miembros de datos. Aquí doy un ejemplo de que los métodos no se anulan:
class A{
public:
virtual void Say(){
std::cout<<"I am A"<<std::endl;
}
};
class B: public A{
public:
void Say() override{
std::cout<<"I am B"<<std::endl;
}
};
int main(){
B b;
A a1;
A a2=b;
b.Say(); // I am B
a1.Say(); // I am A
a2.Say(); // I am A why???
}
B (objeto b) se deriva de A (objeto a1 y a2). by a1, como esperamos, llaman a su función miembro. Pero desde el punto de vista del polimorfismo, no esperamos que a2, que es asignado por b, no sea anulado. Básicamente, a2 solo guarda una parte de clase A de b y eso es la división de objetos en C ++.
Para resolver este problema, se debe utilizar una referencia o un puntero.
A& a2=b;
a2.Say(); // I am B
o
A* a2 = &b;
a2->Say(); // I am B
Para más detalles, vea mi publicación.
La estrella de HGTV, Christina Hall, revela que le diagnosticaron envenenamiento por mercurio y plomo, probablemente debido a su trabajo como manipuladora de casas.
Recientemente salió a la luz un informe policial que acusa a la estrella de 'Love Is Blind', Brennon, de violencia doméstica. Ahora, Brennon ha respondido a los reclamos.
Conozca cómo Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia mientras organizaba la primera celebración de Acción de Gracias desde que murió su madre, Naomi Judd.
Descubra por qué un destacado experto en lenguaje corporal cree que es fácil trazar "tales paralelismos" entre la princesa Kate Middleton y la princesa Diana.
Los inodoros arrojan columnas de aerosol invisibles con cada descarga. ¿Como sabemos? La prueba fue capturada por láseres de alta potencia.
Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?
The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!
¿Sigue siendo efectivo ese lote de repelente de insectos que te quedó del verano pasado? Si es así, ¿por cuánto tiempo?
Anteriormente, Kotaku informó que un hotel Godzilla se estaba abriendo en Tokio este abril. Junto al hotel, estaba programada la aparición de una enorme cabeza de 'Zilla, pero todo lo que hemos visto fueron imágenes conceptuales computarizadas.
Foto: Getty Desde que lanzó The Boring Company hace un año, Elon Musk ha mencionado varios sitios de construcción posibles para el negocio de perforación de túneles y ha descartado una vaga referencia a una aprobación gubernamental "verbal" para un túnel Hyperloop que conecta la ciudad de Nueva York y Washington. , CC. Pero ahora sabemos que al menos un alcalde quiere que Musk perfore un agujero debajo de su ciudad.
Tapas elásticas de silicona de Tomorrow's Kitchen, paquete de 12 | $14 | Amazonas | Código promocional 20OFFKINJALids son básicamente los calcetines de la cocina; siempre perdiéndose, dejando contenedores huérfanos que nunca podrán volver a cerrarse. Pero, ¿y si sus tapas pudieran estirarse y adaptarse a todos los recipientes, ollas, sartenes e incluso frutas en rodajas grandes que sobran? Nunca más tendrás que preocuparte por perder esa tapa tan específica.
Hemos pirateado algunas ciudades industriales en esta columna, como Los Ángeles y Las Vegas. Ahora es el momento de una ciudad militar-industrial-compleja.
El equipo está a la espera de las medallas que ganó en los Juegos Olímpicos de Invierno de 2022 en Beijing, ya que se está resolviendo un caso de dopaje que involucra a la patinadora artística rusa Kamila Valieva.
Miles de compradores de Amazon recomiendan la funda de almohada de seda Mulberry, y está a la venta en este momento. La funda de almohada de seda viene en varios colores y ayuda a mantener el cabello suave y la piel clara. Compre las fundas de almohada de seda mientras tienen hasta un 46 por ciento de descuento en Amazon
El jueves se presentó una denuncia de delito menor amenazante agravado contra Joe Mixon.
El Departamento de Policía de Lafayette comenzó a investigar a un profesor de la Universidad de Purdue en diciembre después de recibir varias denuncias de un "hombre sospechoso que se acercaba a una mujer".
Al igual que el mundo que nos rodea, el lenguaje siempre está cambiando. Mientras que en eras anteriores los cambios en el idioma ocurrían durante años o incluso décadas, ahora pueden ocurrir en cuestión de días o incluso horas.
Estoy de vuelta por primera vez en seis años. No puedo decirte cuánto tiempo he estado esperando esto.
“And a river went out of Eden to water the garden, and from thence it was parted and became into four heads” Genesis 2:10. ? The heart is located in the middle of the thoracic cavity, pointing eastward.
Creo, un poco tarde en la vida, en dar oportunidades a la gente. Generosamente.