¿Qué es el corte de objetos?


Respuestas:


609

"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 la cual se "corta".

Por ejemplo,

class A {
   int foo;
};

class B : public A {
   int bar;
};

Entonces, un objeto de tipo Btiene dos miembros de datos, fooy bar.

Entonces, si fueras a escribir esto:

B b;

A a = b;

Entonces la información bsobre el miembro barse pierde en a.


66
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 Conrad

55
Interesante. He estado programando en C ++ durante 15 años y este problema nunca se me ocurrió, ya que siempre he pasado objetos por referencia como una cuestión de eficiencia y estilo personal. Muestra cómo los buenos hábitos pueden ayudarte.
Karl Bielefeldt

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

37
Esto no es "rebanar", o al menos una variante benigna de él. El verdadero problema ocurre si lo haces B b1; B b2; A& b2_ref = b2; b2 = b1. Se podría pensar que haya copiado b1a b2, pero que no tienen! Ha copiado una parte de b1a b2(la parte de la b1que Bheredó de A), y dejó las otras partes de b2cambios. b2ahora es una criatura frankensteiniana que consta de unos pocos fragmentos b1seguidos de algunos fragmentos de b2. Ugh! Voto negativo porque creo que la respuesta es muy engañosa.
fgp

24
@fgp Su comentario debería leer B b1; B b2; A& b2_ref = b2; b2_ref = b1" El verdadero problema se produce si usted " ... deriva de una clase con un operador de asignación no virtual. ¿ AIncluso está destinado a la derivación? No tiene funciones virtuales. Si deriva de un tipo, ¡tiene que lidiar con el hecho de que sus funciones miembro pueden ser llamadas!
curioso

510

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, como las otras respuestas, que está tratando con dos clases Ay de Bdónde Bderiva (públicamente) A.

En esta situación, C ++ permite pasar una instancia de Ba A's operador de asignación (y también al constructor de copia). Esto funciona porque una instancia de Bse puede convertir en a const A&, que es lo que los operadores de asignación y los constructores de copias esperan que sean sus argumentos.

El caso benigno

B b;
A a = b;

No sucede nada malo: solicitó una instancia de la Acual es una copia B, y eso es exactamente lo que obtiene. Claro, ano contendrá algunos de blos miembros de, pero ¿cómo debería? Es un A, después de todo, no un B, por lo que ni siquiera ha oído hablar de estos miembros, y mucho menos podría almacenarlos.

El traicionero caso

B b1;
B b2;
A& a_ref = b2;
a_ref = b1;
//b2 now contains a mixture of b1 and b2!

Puede pensar que b2será una copia de b1después. Pero, por desgracia, es no ! Si lo inspeccionas, descubrirás que b2es una criatura frankensteiniana, hecha de algunos trozos de b1(los trozos que Bhereda de A), y algunos trozos de b2(los trozos que solo Bcontiene). ¡Ay!

¿Que pasó? Bueno, C ++ por defecto no trata a los operadores de asignación como virtual. Por lo tanto, la línea a_ref = b1llamará al operador de asignación de A, no al de B. Esto se debe a que, para funciones no virtuales, el tipo declarado (formalmente: estático ) (que es A&) determina qué función se llama, en oposición al tipo real (formalmente: dinámico ) (que sería B, ya que hace a_refreferencia a una instancia de B) . Ahora, Ael operador de asignación obviamente conoce solo los miembros declarados A, por lo que copiará solo esos, dejando los miembros agregados Bsin cambios.

Una solución

Asignar solo a partes de un objeto generalmente tiene poco sentido, pero C ++, desafortunadamente, no proporciona una forma integrada de prohibir esto. Sin embargo, puedes rodar el tuyo. 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 usar dynamic_castpara verificar que el objeto asignado tenga un tipo compatible. El tercer paso es hacer la asignación real en un miembro (protegida!) assign(), Ya que B's assign()probablemente va a querer usar AEs 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.


12
En mi humilde opinión, el problema es que hay dos tipos diferentes de sustituibilidad que pueden estar implícitos en la herencia: derivedse puede dar cualquier valor al código que espera un basevalor, o se puede usar cualquier referencia derivada 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 debe ser sustituible por una referencia base, pero las instancias derivadas no deben ser sustituibles por las base; También hay muchos casos en los que las instancias deberían ser convertibles, pero las referencias no deberían sustituir.
supercat

16
No entiendo qué es tan malo en su caso "traicionero". Declaró que desea: 1) obtener una referencia a un objeto de la clase A y 2) lanzar 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 pequeño marco de imagen (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, se ve bastante bien, tal como quería el pintor, ¿verdad? :)
Mladen B.

12
El problema es, de manera diferente, que C ++ por defecto asume un tipo de sustituibilidad muy fuerte : requiere que las operaciones de la clase base funcionen correctamente en instancias de 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 a este respecto, también debe deshabilitar explícitamente las incorrectas generadas por el compilador. O, por supuesto, manténgase alejado de la herencia pública, que generalmente es una buena sugerencia de todos modos ;-)
fgp

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

13
¿Que? No tenía idea de que los operadores podrían ser marcados virtuales
Paul

154

Si tiene una clase base Ay 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 wantAnAnecesita una copia de derived. Sin embargo, el objeto derivedno se puede copiar completamente, ya que la clase Bpodrí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 desea crear, porque

  • puede estar incompleto
  • se comporta como un objeto A( Bse pierde todo el comportamiento especial de la clase ).

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

83
@fgp: Es sorprendente, porque no pasa una A a la función.
Black

10
@fgp: El comportamiento es similar. Sin embargo, para el programador promedio de C ++ podría ser menos obvio. Hasta donde entendí la pregunta, nadie se está "quejando". Se trata solo de cómo el compilador maneja la situación. En mi humilde opinión, es mejor evitar el corte en absoluto pasando referencias (const).
Negro

99
@ThomasW No, no tiraría la herencia, pero usaría referencias. Si la firma de wantAnA sería nula wantAnA (const A y myA) , entonces no había habido cortes. En cambio, se pasa una referencia de solo lectura al objeto del llamante.
Negro

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

41

Estas son todas 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());
}

El resultado es:

Call by reference
I am a 'B'

Call by copy
'A' copy constructor
I am an 'A'

Hola. Gran respuesta pero tengo una pregunta. Si hago algo como esto ** dev d; base * b = & d; ** ¿También se realiza el corte?
Adrian

@Adrian Si introduce algunas funciones miembro nuevas o variables miembro en la clase derivada, entonces no se puede acceder a ellas directamente desde el puntero de la clase base. Sin embargo, aún puede acceder a ellas desde dentro de las funciones virtuales de clase base sobrecargadas. Vea esto: godbolt.org/z/LABx33
Vishal Sharma

30

La tercera coincidencia en google para "C ++ slicing" 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 informarnos qué más está buscando.


29

El problema de 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 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.


3
Por favor explique cómo puede ocurrir la corrupción de la memoria.
miedo el

44
Olvidé que la copiadora restablecerá el vptr, mi error. Pero aún puede obtener corrupción si A tiene un puntero y B lo configura para que apunte a la sección de B que se corta.
Walter Bright

18
Este problema no solo se limita al corte. Cualquier clase que contenga punteros tendrá un comportamiento dudoso con un operador de asignación predeterminado y un constructor de copias.
Weeble

2
@Weeble: es por eso que anula el destructor predeterminado, el operador de asignación y el constructor de copias en estos casos.
Bjarke Freund-Hansen

77
@Weeble: lo que hace que el corte de objetos sea peor que las reparaciones generales de punteros es que, para asegurarse de que ha evitado que se corte, una clase base debe proporcionar constructores de conversión para cada clase derivada . (¿Por qué? Cualquier clase derivada que se pierda es susceptible de ser recogida por el copiador de la clase base, ya que Derivedes convertible implícitamente a Base). Esto obviamente es contrario al Principio Abierto-Cerrado, y una gran carga de mantenimiento.
j_random_hacker

11

En C ++, un objeto de clase derivada puede asignarse a un objeto de clase base, pero a la inversa no es posible.

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 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.


8

El problema de corte en C ++ surge de la semántica de valor de sus objetos, que se mantuvo principalmente debido a la compatibilidad con estructuras de C. Debe usar una referencia explícita o una sintaxis de puntero para lograr un comportamiento de objeto "normal" que se encuentra en la mayoría de los otros lenguajes que hacen objetos, es decir, los objetos siempre se pasan por referencia.

Las respuestas cortas son 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 de valor, el corte es un comportamiento razonable y tiene usos relativamente raros, que no existen en la mayoría de los otros idiomas. Algunas personas lo consideran una característica de C ++, mientras que muchos lo consideran una de las peculiaridades / errores de C ++.


55
" comportamiento de objeto" normal " que no es" comportamiento de objeto normal ", eso es semántico de referencia . Y no se relaciona de ninguna manera con C struct, compatibilidad u otra falta de sentido que cualquier sacerdote aleatorio de OOP te dijo.
curioso

44
@curiousguy Amén, hermano. Es triste ver con qué frecuencia C ++ es criticado por no ser Java, cuando la semántica de valores es una de las cosas que hace que C ++ sea tan increíblemente poderoso.
fgp

Esto no es una característica, no es un capricho / error. Es un comportamiento normal de copia en pila, ya que llamar a una función con una variable de tipo arg o la misma asignación de pila Basedebe tomar exactamente sizeof(Base)bytes en la memoria, con una posible alineación, tal vez, es por eso que "asignación" (copia en pila ) no copiará a los miembros de clase derivados, sus desplazamientos están fuera de sizeof. Para evitar la "pérdida de datos", simplemente use el puntero, como cualquier otra persona, ya que la memoria del puntero está fija en su lugar y tamaño, mientras que la pila es muy volátil
Croll

Definitivamente una mala característica de C ++. Asignar un objeto derivado a un objeto base debería estar prohibido, mientras que vincular un objeto derivado a una referencia o un puntero de la clase base debería estar bien.
John Z. Li

7

Entonces ... ¿Por qué es malo perder la información derivada? ... porque el autor de la clase derivada puede haber cambiado la representación de modo 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 a la representación base.

También pensé que alguien debería mencionar lo que debe hacer para evitar cortar ... Obtenga una copia de los estándares de codificación de C ++, 101 pautas de reglas y las mejores prácticas. Tratar con rebanar es el # 54.

Sugiere un patrón algo sofisticado para tratar completamente el problema: tener un constructor de copia protegida, 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 explícita, lo que permite un corte explícito si se desea.


3
" También puede marcar el constructor de copia en la base explícita ", lo que no ayuda en absoluto.
curioso

6

1. LA DEFINICIÓN DEL PROBLEMA DE CORTE

Si D es una clase derivada de la clase base B, puede asignar un objeto de tipo Derivado 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 mascota variable pierde su campo de raza. Esto se llama el problema de corte .

2. CÓMO ARREGLAR EL PROBLEMA DE CORTE

Para vencer el problema, utilizamos 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, ninguno de los miembros de datos o funciones de miembro de la variable dinámica a la que apunta ptrD (objeto de clase descendiente) se perderá. Además, si necesita usar funciones, la función debe ser una función virtual.


77
Entiendo la parte de "corte", pero no entiendo el "problema". ¿Cómo es un problema que algún estado de dogeso no sea parte de la clase Pet(el breedmiembro de datos) no se copie en la variable pet? El código solo está interesado en los Petmiembros de datos, aparentemente. Rebanar es definitivamente un "problema" si no es deseado, pero no lo veo aquí.
curioso

44
" ((Dog *)ptrP)" Sugiero usarstatic_cast<Dog*>(ptrP)
curiousguy

Sugiero señalar que hará que la cadena 'se reproduzca' eventualmente pierda memoria sin un destructor virtual (no se llamará al destructor de 'cadena') al eliminar a través de 'ptrP' ... ¿Por qué es problemático lo que muestra? La solución es principalmente un diseño de clase adecuado. El problema en este caso es que escribir constructores para controlar la visibilidad al heredar es tedioso y fácil de olvidar. No se acercará a la zona de peligro con su código, ya que no hay polimorfismo involucrado o mencionado (el corte truncará su objeto pero no hará que su programa se bloquee, aquí).
Amigo

24
-1 Esto no explica completamente el problema real. C ++ tiene una semántica de valor, no una semántica de referencia como Java, por lo que todo esto es completamente de esperar. Y la "solución" realmente es un ejemplo de código C ++ verdaderamente horrible . "Arreglar" problemas no existentes como este tipo de segmentación recurriendo a la asignación dinámica es una receta para códigos defectuosos, pérdida de memoria y un rendimiento horrible. Tenga en cuenta que no son los casos en corte es mala, pero esta respuesta failes para que apunte a cabo. Sugerencia: el problema comienza si asigna a través de referencias .
fgp

¿Entiendes que intentar acceder a un miembro de tipo que no está definido ( Dog::breed) no es un ERROR relacionado con SLICING?
Croll

4

Me parece que cortar en rodajas no es tanto un problema que no sea 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 tenerlo en cuenta y conocerlo internamente, el método llamado solo funcionará con el objeto de superclase (también conocido como clase base).

Me parece solo la expectativa irracional de que proporcionar una subclase donde se solicita una clase base, de alguna manera resultaría en resultados específicos de la 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 generalmente es el resultado de sacrificar un buen diseño de OOP en favor de la conveniencia o las ganancias de rendimiento.


3
Pero recuerda, Minok, que NO estás pasando una referencia de ese objeto. Estás pasando una NUEVA copia de ese objeto, pero estás usando la clase base para copiarlo en el proceso.
Arafangion

copia protegida / asignación en la clase base y este problema está resuelto.
Amigo

1
Tienes razón. Una buena práctica es usar clases base abstractas o restringir el acceso a la copia / asignación. Sin embargo, no es tan fácil detectarlo una vez que está allí y es fácil olvidar cuidarlo. Llamar a métodos virtuales con * en rodajas puede hacer que sucedan cosas misteriosas si se escapa sin una infracción de acceso.
Amigo

1
Recuerdo de mis cursos de programación de C ++ en la universidad que había mejores prácticas permanentes que para cada clase que creamos, teníamos que escribir constructores predeterminados, copiar constructores y operadores de asignación, así como un destructor. De esta manera, se aseguró de que la construcción de copias y cosas similares sucedieran de la manera que lo necesitaba, mientras escribía la clase ... en lugar de más tarde, apareciendo algún comportamiento extraño.
Minok

3

OK, lo intentaré después de leer muchas publicaciones que explican la división de objetos, pero no cómo se vuelve problemático.

El escenario vicioso que puede provocar daños en la memoria es el siguiente:

  • La clase proporciona una asignación (accidentalmente, posiblemente generada por el compilador) en una clase base polimórfica.
  • El cliente copia y corta una instancia de una clase derivada.
  • El cliente llama a una función miembro virtual que accede al estado cortado.

3

La división 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 clase base no saben nada sobre el derivado, solo se copia la parte base del derivado. Esto se conoce comúnmente como rebanar.


1
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; 
}

44
¿Te importaría dar algunos detalles adicionales? ¿Cómo difiere su respuesta de las ya publicadas?
Alexis Pigeon

2
Supongo que más explicación no sería mala.
Looper

-1

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 (descartan) del objeto de clase base.

class Base { 
int x;
 };

class Derived : public Base { 
 int z; 
 };

 int main() 
{
Derived d;
Base b = d; // Object Slicing,  z of d is sliced off
}

-1

Cuando un objeto de clase derivada se asigna a un objeto de clase base, todos los miembros del objeto de clase derivada se copian en un objeto de clase base, excepto los miembros que no están presentes en la clase base. Estos miembros son cortados por el compilador. Esto se llama segmentación de objetos.

Aquí hay un ejemplo:

#include<bits/stdc++.h>
using namespace std;
class Base
{
    public:
        int a;
        int b;
        int c;
        Base()
        {
            a=10;
            b=20;
            c=30;
        }
};
class Derived : public Base
{
    public:
        int d;
        int e;
        Derived()
        {
            d=40;
            e=50;
        }
};
int main()
{
    Derived d;
    cout<<d.a<<"\n";
    cout<<d.b<<"\n";
    cout<<d.c<<"\n";
    cout<<d.d<<"\n";
    cout<<d.e<<"\n";


    Base b = d;
    cout<<b.a<<"\n";
    cout<<b.b<<"\n";
    cout<<b.c<<"\n";
    cout<<b.d<<"\n";
    cout<<b.e<<"\n";
    return 0;
}

Generará:

[Error] 'class Base' has no member named 'd'
[Error] 'class Base' has no member named 'e'

Votado abajo porque ese no es un buen ejemplo. Tampoco funcionaría si en lugar de copiar d a b, usara un puntero en cuyo caso d y e seguirían existiendo pero Base no tiene esos miembros. Su ejemplo solo muestra que no puede acceder a miembros que la clase no tiene.
Stefan Fabian

-2

Acabo de encontrarme con el problema del corte y rápidamente llegué aquí. Así que déjame agregar mis dos centavos a esto.

Veamos un ejemplo del "código de producción" (o algo parecido):


Digamos que tenemos algo que despacha acciones. Una interfaz de usuario del centro de control, por ejemplo.
Esta interfaz de usuario debe obtener una lista de las cosas que se pueden enviar actualmente. Entonces definimos una clase que contiene la información de despacho. Digamos que es Action. Entonces an Actiontiene algunas variables miembro. Por simplicidad solo tenemos 2, siendo a std::string namey a std::function<void()> f. Luego tiene un void activate()que simplemente ejecuta al fmiembro.

Entonces la interfaz de usuario obtiene un std::vector<Action>suministro. Imagine algunas funciones como:

void push_back(Action toAdd);

Ahora hemos establecido cómo se ve desde la perspectiva de la interfaz de usuario. No hay problema hasta ahora. Pero otro tipo que trabaja en este proyecto de repente decide que hay acciones especializadas que necesitan más información en elAction objeto. Por qué razón alguna vez. Eso también podría resolverse con capturas lambda. Este ejemplo no se toma 1-1 del código.

Entonces el chico deriva de Actionagregar su propio sabor.
Él pasa una instancia de su clase casera a la push_backpero luego el programa se vuelve loco.

¿Entonces qué pasó?
Como usted podría haber adivinado: el objeto ha sido cortada.

La información adicional de la instancia se ha perdido y fahora es propensa a comportamientos indefinidos.


Espero que este ejemplo arroje luz para aquellas personas que realmente no pueden imaginarse las cosas cuando hablan de que As y Bs se derivan de alguna manera.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.