¿Qué es el corte de objetos?

11 minutos de lectura

¿Que es el corte de objetos
Frankomanía

Alguien lo mencionó en el IRC como el problema del corte.

¿Que es el corte de objetos
david dibben

“Cortar” es donde 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;
};

Así que 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 en b sobre el miembro bar se pierde en a.

  • Muy informativo, pero consulte stackoverflow.com/questions/274626#274636 para ver un ejemplo de cómo se produce el corte durante las llamadas a métodos (lo que subraya el peligro un poco mejor que el ejemplo de asignación simple).

    – Blair Conrado

    8 de noviembre de 2008 a las 13:53

  • Interesante. Llevo 15 años programando en C++ y nunca se me ocurrió este tema, ya que siempre he pasado objetos por referencia por una cuestión de eficiencia y estilo personal. Va a mostrar cómo los buenos hábitos pueden ayudarte.

    -Karl Bielefeldt

    2 de febrero de 2011 a las 3:48

  • @Felix Gracias, pero no creo que la conversión (ya que no es una aritmética de puntero) funcione, A a = b; a ahora es objeto de tipo A que tiene copia de B::foo. Será un error devolverlo ahora, creo.

    usuario72424

    12 de agosto de 2011 a las 12:27

  • Esto no es “rebanar”, o al menos una variante benigna de ello. El problema real ocurre si lo hace B b1; B b2; A& b2_ref = b2; b2 = b1. Podrías pensar que has copiado b1 para b2, ¡pero no lo has hecho! Has copiado un parte de b1 para b2 (la parte de b1 que B heredado de A), y dejó las otras partes de b2 sin alterar. b2 ahora es una criatura frankensteiniana que consta de unos pocos fragmentos de b1 seguido de algunos trozos de b2. ¡Puaj! Downvoting porque creo que la respuesta es muy engañosa.

    – fgp

    22 de enero de 2013 a las 14:07


  • @fgp Tu comentario debería leer B b1; B b2; A& b2_ref = b2; b2_ref = b1El verdadero problema ocurre si Ud.“… derivan de una clase con un operador de asignación no virtual. Es A incluso destinado a la derivación? No tiene funciones virtuales. Si derivas de un tipo, ¡tienes que lidiar con el hecho de que sus funciones miembro pueden ser llamadas!

    – chico curioso

    29 de junio de 2013 a las 14:54

1647674290 873 ¿Que es el corte de objetos
fgp

La mayoría de las respuestas aquí no explican cuál es el problema real con el corte. Solo explican los casos benignos de rebanado, no los traicioneros. Suponga, como las otras respuestas, que está tratando con dos clases A y Bdonde B deriva (públicamente) de A.

En esta situación, C++ le permite pasar una instancia de B para Aoperador de asignación (y también al constructor de copias). Esto funciona porque una instancia de B se puede convertir en un const A&que es lo que los operadores de asignación y los constructores de copia esperan que sean sus argumentos.

El caso benigno

B b;
A a = b;

No pasa nada malo allí: usted pidió una instancia de A que es una copia de B, y eso es exactamente lo que obtienes. Por supuesto, a no contendrá algo de b‘s miembros, pero ¿cómo debería? Se trata de un Adespués de todo, no es un Bpor lo que ni siquiera ha oyó sobre estos miembros, y mucho menos sería capaz de almacenarlos.

El caso traicionero

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, ¡ay!, es no! Si lo inspeccionas, descubrirás que b2 es una criatura frankensteiniana, hecha de algunos trozos de b1 (los trozos que B hereda de 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. Así, la línea a_ref = b1 llamará al operador de asignación de Ano el de B. Esto se debe a que, para las funciones no virtuales, el declarado (formalmente: estático) tipo (que es A&) determina qué función se llama, a diferencia de real (formalmente: dinámica) tipo (que sería Bya que a_ref hace referencia a una instancia de B). Ahora, AEl operador de asignación de obviamente solo conoce los miembros declarados en Apor lo que copiará solo esos, dejando los miembros agregados en B sin alterar.

Una solución

Asignar solo partes de un objeto generalmente tiene poco sentido, pero C ++, desafortunadamente, no proporciona una forma integrada de prohibir esto. Sin embargo, puede rodar el suyo propio. El primer paso es hacer que el operador de asignación virtual. Esto garantizará que siempre sea el real operador de asignación del tipo que se llama, no el declarado tipo 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() 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 covariantemente el tipo de retorno, ya que sabe que está devolviendo una instancia de B.

  • En mi humilde opinión, el problema es que hay dos tipos diferentes de sustituibilidad que pueden estar implícitas en la herencia: cualquier derived Se puede dar valor al código que espera un base valor, o cualquier referencia derivada se puede utilizar como referencia base. Me gustaría ver un lenguaje con un sistema de tipos que aborde ambos conceptos por separado. Hay muchos casos en los que una referencia derivada debería ser sustituible por una referencia base, pero las instancias derivadas no deberían ser sustituibles por las base; también hay muchos casos en los que las instancias deben ser convertibles pero las referencias no deben sustituir.

    – Super gato

    12 de agosto de 2013 a las 16:11


  • No entiendo qué tiene de malo tu caso “traidor”. Usted declaró que desea: 1) obtener una referencia a un objeto de la clase A y 2) enviar el objeto b1 a la clase A y copiar sus cosas a una referencia de la clase A. Lo que realmente está mal aquí es la lógica adecuada detrás el código dado. En otras palabras, tomó un marco de imagen pequeño (A), lo colocó sobre una imagen más grande (B) y pintó a través de ese marco, quejándose más tarde de que su imagen más grande ahora se ve fea 🙂 Pero si solo consideramos esa área enmarcada, queda bastante bien, tal y como queria el pintor, no? 🙂

    – Mladen B.

    14 de noviembre de 2013 a las 13:05

  • El problema es, dicho de otro modo, que C++ por defecto asume un tipo muy fuerte de sustituibilidad – requiere que las operaciones de la clase base funcionen correctamente en las instancias de la subclase. Y eso incluso para operaciones que el compilador generó automáticamente como asignación. Por lo tanto, no es suficiente no arruinar sus propias operaciones en este sentido, también debe deshabilitar explícitamente las incorrectas generadas por el compilador. O, por supuesto, manténgase alejado de la herencia pública, que suele ser una buena sugerencia de todos modos 😉

    – fgp

    16 de noviembre de 2013 a las 16:31

  • Otro enfoque común es simplemente deshabilitar el operador de copia y asignación. Para las clases dentro de la jerarquía de herencia, normalmente no hay razón para usar valor en lugar de referencia o puntero.

    –Siyuan Ren

    22 de agosto de 2014 a las 10:48

  • ¿Que? No tenía idea de que los operadores pudieran marcarse como virtuales

    – Paula

    2 de marzo de 2015 a las 15:23

1647674290 467 ¿Que es el corte de objetos
Negro

Si tienes una clase base A y una clase derivada Bentonces 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 completamente, ya que la clase B podría inventar variables miembro adicionales que no están en su clase base A.

Por lo tanto, 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

  • puede estar incompleto,
  • se comporta como un A-objeto (todo el comportamiento especial de la clase B está perdido).

  • C++ es no ¡Java! Si wantAnA (¡como su nombre lo indica!) quiere un A, entonces eso es lo que se obtiene. Y una instancia de Ase comportará como un A. ¿Cómo es eso sorprendente?

    – fgp

    22 de enero de 2013 a las 16:39

  • @fgp: Es sorprendente, porque tú no pases una a a la función.

    – Negro

    3 de marzo de 2013 a las 7:03

  • @fgp: el comportamiento es similar. Sin embargo, para el programador promedio de C++ puede ser menos obvio. Por lo que entendí la pregunta, nadie se “queja”. Se trata solo de cómo el compilador maneja la situación. En mi humilde opinión, es mejor evitar el corte pasando referencias (const).

    – Negro

    7 de abril de 2013 a las 12:13

  • @ThomasW No, no descartaría la herencia, pero usaría referencias. Si la firma de wantAnA fuera void wantAnA(const A & myA), entonces no había habido corte. En su lugar, se pasa una referencia de solo lectura al objeto de la persona que llama.

    – Negro

    28 de mayo de 2013 a las 7:44

  • el problema radica principalmente en la conversión automática que realiza el compilador desde derived al tipo A. La conversión implícita siempre es una fuente de comportamiento inesperado en C++, porque a menudo es difícil entender al observar el código localmente que se realizó una conversión.

    – pqnet

    06/08/2014 a las 23:15

Estas son todas buenas respuestas. Solo me gustaría agregar un ejemplo de ejecución al pasar objetos por valor frente a 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'

1647674290 238 ¿Que es el corte de objetos
El Pablo arquetípico

La tercera coincidencia en google para “corte en C ++” me da este artículo de Wikipedia http://en.wikipedia.org/wiki/Object_slicing y esto (calentado, pero las primeras publicaciones definen el problema): http://bytes.com/forum/thread163565.html

Entonces es cuando asignas 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 informarnos qué más está buscando.

El problema del corte es serio porque puede resultar en corrupción de 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 derivadas 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 la basura.

¿Que es el corte de objetos
Kartik Maheshwari

En C++, un objeto de clase derivada se puede asignar a un objeto de clase base, pero no es posible hacerlo de otra manera.

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
}

El corte de objetos ocurre cuando un objeto de clase derivada se asigna a un objeto de clase base, los atributos adicionales de un objeto de clase derivada se cortan para formar el objeto de clase base.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad