O que é corte de objetos?

Respostas:

609

"Fatiar" é onde você atribui um objeto de uma classe derivada a uma instância de uma classe base, perdendo parte das informações - algumas delas são "fatiadas".

Por exemplo,

class A {
   int foo;
};

class B : public A {
   int bar;
};

Portanto, um objeto do tipo Btem dois membros de dados fooe bar.

Então, se você escrever isso:

B b;

A a = b;

Em seguida, as informações bsobre o membro barsão perdidas a.

David Dibben
fonte
66
Muito informativo, mas consulte stackoverflow.com/questions/274626#274636 para obter um exemplo de como ocorre o fatiamento durante as chamadas de método (o que ressalta o perigo um pouco melhor do que o exemplo de atribuição simples).
Blair Conrad
55
Interessante. Venho programando em C ++ há 15 anos e esse problema nunca me ocorreu, pois sempre passei objetos por referência como questão de eficiência e estilo pessoal. Mostra como bons hábitos podem ajudá-lo.
Karl Bielefeldt
10
@ Felix Obrigado, mas eu não acho que lançar de volta (já que não é uma aritmética de ponteiro) funcionará, A a = b; aagora é objeto do tipo Aque possui cópia B::foo. Será um erro devolvê-lo agora, eu acho.
37
Isso não é "fatiar", ou pelo menos uma variante benigna. O verdadeiro problema ocorre se você o fizer B b1; B b2; A& b2_ref = b2; b2 = b1. Você pode pensar que você copiou b1para b2, mas você não tem! Você copiou uma parte do b1que b2(a parte b1que Bherdou de A), e deixou as outras partes b2inalteradas. b2é agora uma criatura frankensteiniana que consiste em alguns pedaços b1seguidos por alguns pedaços de b2. Ugh! Voto negativo, porque acho que a resposta é muito enganadora.
Fgp
24
@fgp Seu comentário deve ser B b1; B b2; A& b2_ref = b2; b2_ref = b1" O verdadeiro problema ocorre se você " ... deriva de uma classe com um operador de atribuição não virtual. É Amesmo destinado a derivação? Não possui funções virtuais. Se você deriva de um tipo, precisará lidar com o fato de que suas funções-membro podem ser chamadas!
precisa
510

A maioria das respostas aqui não consegue explicar qual é o problema real com o fatiamento. Eles apenas explicam os casos benignos de fatiar, não os traiçoeiros. Suponha, como as outras respostas, que você está lidando com duas classes Ae B, de onde Bderiva (publicamente) A.

Nesta situação, C ++ permite que você passe uma instância Bde A's operador de atribuição (e também para o construtor de cópia). Isso funciona porque uma instância de Bpode ser convertida em a const A&, que é o que os operadores de atribuição e os construtores de cópias esperam que seus argumentos sejam.

O caso benigno

B b;
A a = b;

Nada de ruim acontece lá - você solicitou uma instância da Aqual é uma cópia Be é exatamente isso que obtém. Claro, anão conterá alguns bmembros, mas como deveria? É um A, afinal, não um B, por isso nem sequer ouviu falar sobre esses membros, e muito menos seria capaz de armazená-los.

O caso traiçoeiro

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

Você pode pensar que b2será uma cópia b1posterior. Mas, infelizmente, é não ! Se você inspecionar, descobrirá que b2é uma criatura frankensteiniana, feita de alguns pedaços de b1(os pedaços que Bherdam A) e alguns pedaços de b2(pedaços que Bcontêm apenas ). Ai!

O que aconteceu? Bem, C ++ por padrão não trata os operadores de atribuição como virtual. Assim, a linha a_ref = b1chamará o operador de atribuição de A, não o de B. Isso ocorre porque, para funções não virtuais, o tipo declarado (formalmente: estático ) (que é A&) determina qual função é chamada, em oposição ao tipo real (formalmente: dinâmico ) (que seria B, pois a_refreferencia uma instância de B) . Agora, Aobviamente, o operador de atribuição sabe apenas sobre os membros declarados A, portanto ele copiará somente aqueles, deixando os membros adicionados Binalterados.

Uma solução

Atribuir apenas partes de um objeto geralmente faz pouco sentido, mas o C ++, infelizmente, não fornece uma maneira interna de proibir isso. Você pode, no entanto, fazer o seu próprio. A primeira etapa é tornar o operador de atribuição virtual . Isso garantirá que sempre seja chamado o operador de atribuição do tipo real , e não o tipo declarado . A segunda etapa é usar dynamic_castpara verificar se o objeto atribuído tem um tipo compatível. O terceiro passo é fazer a atribuição real em um (protegida!) Membro assign(), uma vez que B's assign()provavelmente vai querer usar Aé assign()para copiar A's, membros.

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

Note-se que, por conveniência pura, Bé operator=covariantemente substitui o tipo de retorno, uma vez que sabe que ele está retornando uma instância B.

fgp
fonte
12
IMHO, o problema é que existem dois tipos diferentes de substituibilidade que podem estar implícitos na herança: qualquer derivedvalor pode ser dado ao código que espera um basevalor ou qualquer referência derivada pode ser usada como referência base. Eu gostaria de ver uma linguagem com um sistema de tipos que aborde os dois conceitos separadamente. Há muitos casos em que uma referência derivada deve ser substituída por uma referência base, mas as instâncias derivadas não devem ser substituídas por referências básicas; também existem muitos casos em que as instâncias devem ser conversíveis, mas as referências não devem substituir.
Supercat
16
Não entendo o que há de tão ruim no seu caso "traiçoeiro". Você declarou que deseja: 1) obter uma referência a um objeto da classe A e 2) converter o objeto b1 na classe A e copiar seu material para uma referência da classe A. O que está realmente errado aqui é a lógica adequada por trás o código fornecido. Em outras palavras, você pegou um pequeno quadro de imagem (A), colocou-o sobre uma imagem maior (B) e o pintou, reclamando mais tarde que sua imagem maior agora parece feia :) Mas se considerarmos apenas essa área emoldurada, parece muito bom, assim como o pintor queria, certo? :)
Mladen B.
12
Em outras palavras, o problema é que o C ++, por padrão, assume um tipo muito forte de substituibilidade - requer que as operações da classe base funcionem corretamente nas instâncias da subclasse. E isso mesmo para operações que o compilador gerou automaticamente como atribuição. Portanto, não basta não estragar suas próprias operações a esse respeito, você também precisa desabilitar explicitamente as incorretas geradas pelo compilador. Ou, claro, ficar longe de herança público, que geralmente é uma boa sugestão anway ;-)
fgp
14
Outra abordagem comum é simplesmente desativar o operador de cópia e atribuição. Para classes dentro da hierarquia de herança, geralmente não há razão para usar valor em vez de referência ou ponteiro.
Siyuan Ren
13
O que? Eu não tinha operadores idéia poderia ser marcado virtual
paulm
154

Se você tiver uma classe base Ae uma classe derivada B, poderá fazer o seguinte.

void wantAnA(A myA)
{
   // work with myA
}

B derived;
// work with the object "derived"
wantAnA(derived);

Agora, o método wantAnAprecisa de uma cópia de derived. No entanto, o objeto derivednão pode ser copiado completamente, pois a classe Bpode inventar variáveis ​​de membro adicionais que não estão em sua classe base A.

Portanto, para chamar wantAnA, o compilador "cortará" todos os membros adicionais da classe derivada. O resultado pode ser um objeto que você não deseja criar, porque

  • pode estar incompleto,
  • ele se comporta como um Aobjeto (todo comportamento especial da classe Bé perdido).
Preto
fonte
41
C ++ não é Java! Se wantAnA(como o próprio nome indica!) Quer um A, então é isso que ele recebe. E uma instância de A, se comportará como uma A. Como isso é surpreendente?
Fgp
83
@fgp: É surpreendente, porque você não passa um A para a função.
Preto
10
@ fgp: O comportamento é semelhante. No entanto, para o programador C ++ médio, pode ser menos óbvio. Até onde eu entendi a pergunta, ninguém está "reclamando". É apenas sobre como o compilador lida com a situação. Imho, é melhor evitar fatiar passando referências (const).
preto
9
@ThomasW Não, eu não descartaria a herança, mas usaria referências. Se a assinatura de wantAnA fosse anulada wantAnA (const A e myA) , não haveria o fatiamento. Em vez disso, uma referência somente leitura ao objeto do chamador é passada.
Preto
14
o problema está principalmente na conversão automática que o compilador executa do derivedtipo A. A conversão implícita é sempre uma fonte de comportamento inesperado no C ++, porque geralmente é difícil entender ao examinar o código localmente que uma conversão ocorreu.
Pqnet # 6/14
41

Todas essas são boas respostas. Gostaria apenas de adicionar um exemplo de execução ao passar objetos por valor vs por referência:

#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());
}

A saída é:

Call by reference
I am a 'B'

Call by copy
'A' copy constructor
I am an 'A'
geh
fonte
Olá. Ótima resposta, mas eu tenho uma pergunta. Se eu fizer algo assim ** dev d; base * b = & d; ** A fatia também ocorre?
Adrian
@Adrian Se você introduzir algumas novas funções ou variáveis ​​de membro na classe derivada, elas não poderão ser acessadas diretamente do ponteiro da classe base. No entanto, você ainda pode acessá-los de dentro das funções virtuais da classe base sobrecarregadas. Veja isto: godbolt.org/z/LABx33
Vishal Sharma
30

A terceira correspondência no google para "C ++ slicing" me fornece este artigo da Wikipedia http://en.wikipedia.org/wiki/Object_slicing e isso (aquecido, mas as primeiras postagens definem o problema): http://bytes.com/ forum / thread163565.html

Então é quando você atribui um objeto de uma subclasse à superclasse. A superclasse não sabe nada das informações adicionais na subclasse e não tem espaço para armazená-las, portanto as informações adicionais são "cortadas".

Se esses links não fornecerem informações suficientes para uma "boa resposta", edite sua pergunta para nos informar o que mais você está procurando.

O Arquetípico Paulo
fonte
29

O problema de fatiar é sério porque pode resultar em corrupção de memória e é muito difícil garantir que um programa não sofra com isso. Para projetá-lo fora do idioma, as classes que suportam herança devem ser acessíveis apenas por referência (não por valor). A linguagem de programação D possui essa propriedade.

Considere a classe A e a classe B derivada de A. A corrupção da memória pode ocorrer se a parte A tiver um ponteiro p e uma instância B que aponte p para os dados adicionais de B. Então, quando os dados adicionais são cortados, p está apontando para o lixo.

Walter Bright
fonte
3
Por favor, explique como a corrupção da memória pode ocorrer.
#
4
Esqueci que o copiador copia o reset do vptr, meu erro. Mas você ainda pode obter corrupção se A tiver um ponteiro e B definir isso para apontar para a seção de B cortada em fatias.
Walter Bright
18
Esse problema não se limita apenas ao fatiamento. Quaisquer classes que contenham ponteiros terão um comportamento duvidoso com um operador de atribuição padrão e um construtor de cópias.
11119 Weeble
2
@ Weeble - É por isso que você substitui o destruidor padrão, o operador de atribuição e o construtor de cópias nesses casos.
Bjarke Freund-Hansen
7
@ Weeble: O que torna o fatiamento de objetos pior do que as correções gerais de ponteiros é que, para ter certeza de que você impediu que o fatiamento acontecesse, uma classe base deve fornecer construtores de conversão para cada classe derivada . (Por quê? Quaisquer classes derivadas perdidas são suscetíveis de serem escolhidas pelo copiador da classe base, uma vez que Derivedé implicitamente conversível em Base.) Isso obviamente é contrário ao Princípio Aberto-Fechado e a uma grande carga de manutenção.
Jrandom_hacker 24/10/12
11

No C ++, um objeto de classe derivado pode ser atribuído a um objeto de classe base, mas a outra maneira não é possível.

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
}

A divisão de objetos ocorre quando um objeto de classe derivada é atribuído a um objeto de classe base, atributos adicionais de um objeto de classe derivada são cortados para formar o objeto de classe base.

Kartik Maheshwari
fonte
8

O problema de fatiar em C ++ surge da semântica de valores de seus objetos, que permaneceu principalmente devido à compatibilidade com estruturas C. Você precisa usar referência explícita ou sintaxe do ponteiro para obter um comportamento "normal" do objeto encontrado na maioria das outras linguagens que fazem objetos, ou seja, os objetos sempre são passados ​​por referência.

A resposta curta é que você divide o objeto atribuindo um objeto derivado a um objeto base por valor , ou seja, o objeto restante é apenas uma parte do objeto derivado. Para preservar a semântica do valor, o fatiamento é um comportamento razoável e tem usos relativamente raros, que não existem na maioria dos outros idiomas. Algumas pessoas o consideram um recurso do C ++, enquanto muitos o consideram uma das peculiaridades / falhas do C ++.

ididak
fonte
5
" comportamento normal do objeto " que não é "comportamento normal do objeto", é semântica de referência . E isso não se relaciona de modo algum com C struct, compatibilidade ou outro absurdo que qualquer padre aleatório de OOP lhe disse.
curiousguy
4
@curiousguy Amém, irmão. É triste ver com que frequência o C ++ é criticado por não ser Java, quando a semântica de valores é uma das coisas que torna o C ++ tão incrivelmente poderoso.
Fgp 22/01
Isso não é um recurso, não é uma peculiaridade / falha de recurso. É um comportamento normal de cópia na pilha, pois a chamada de uma função com uma variável de tipo arg ou (mesma) alocação de pilha Basedeve levar exatamente sizeof(Base)bytes na memória, com possível alinhamento, talvez por isso "atribuição" (cópia na pilha) ) não copiará os membros da classe derivada, suas compensações estão fora do tamanho Para evitar a "perda de dados", ponteiro uso apenas, como qualquer outra pessoa, desde que a memória ponteiro é fixado no local e tamanho, enquanto a pilha é muito volátil
Croll
Definitivamente uma falha de funcionalidade do C ++. A atribuição de um objeto derivado a um objeto base deve ser banida, enquanto a vinculação de um objeto derivado a uma referência ou a um ponteiro da classe base deve estar OK.
John Z. Li
7

Então ... Por que perder as informações derivadas é ruim? ... porque o autor da classe derivada pode ter alterado a representação de tal modo que cortar as informações extras altere o valor que está sendo representado pelo objeto. Isso pode acontecer se a classe derivada for usada para armazenar em cache uma representação que seja mais eficiente para determinadas operações, mas dispendiosa para voltar à representação base.

Também pensei que alguém deveria mencionar o que você deve fazer para evitar o fatiamento ... Obtenha uma cópia dos C ++ Coding Standards, das 101 diretrizes de regras e das melhores práticas. Lidar com fatiar é o número 54.

Ele sugere um padrão um tanto sofisticado para lidar completamente com o problema: ter um construtor de cópias protegidas, um DoClone virtual puro protegido e um Clone público com uma declaração que informará se uma classe (mais) derivada falhou em implementar o DoClone corretamente. (O método Clone faz uma cópia profunda adequada do objeto polimórfico.)

Você também pode marcar o construtor de cópia na base como explícito, o que permite fatias explícitas, se desejado.

Steve Steiner
fonte
3
" Você também pode marcar o construtor de cópia na base como explícito ", o que não ajuda em nada.
precisa
6

1. A DEFINIÇÃO DE PROBLEMA DE CORTE

Se D for uma classe derivada da classe base B, você poderá atribuir um objeto do tipo Derivado a uma variável (ou parâmetro) do tipo Base.

EXEMPLO

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

Embora a atribuição acima seja permitida, o valor atribuído à variável pet perde seu campo de criação. Isso é chamado de problema de corte .

2. COMO SOLUCIONAR O PROBLEMA DE SLICING

Para derrotar o problema, usamos ponteiros para variáveis ​​dinâmicas.

EXEMPLO

Pet *ptrP;
Dog *ptrD;
ptrD = new Dog;         
ptrD->name = "Tommy";
ptrD->breed = "Kangal Dog";
ptrP = ptrD;
cout << ((Dog *)ptrP)->breed; 

Nesse caso, nenhum membro de dados ou função de membro da variável dinâmica apontada pelo ptrD (objeto de classe descendente) será perdido. Além disso, se você precisar usar funções, a função deverá ser uma função virtual.

haberdar
fonte
7
Entendo a parte "fatiar", mas não entendo o "problema". Como é um problema que algum estado dogque não faz parte da classe Pet(o breedmembro de dados) não seja copiado na variável pet? O código é apenas interessado nos Petmembros dos dados - aparentemente. Cortar é definitivamente um "problema" se não for desejado, mas não vejo isso aqui.
curiousguy
4
" ((Dog *)ptrP)" Sugiro usarstatic_cast<Dog*>(ptrP)
curiousguy
Sugiro salientar que você fará com que a string 'race' eventualmente vaze memória sem um destruidor virtual (o destruidor de 'string' não será chamado) ao excluir através de 'ptrP' ... Por que o que você mostra é problemático? A correção é principalmente o design de classe adequado. O problema neste caso é que escrever construtores para controlar a visibilidade ao herdar é tedioso e facilmente esquecido. Você não chegará nem perto da zona de perigo com o seu código, pois não há polimorfismo envolvido ou mesmo mencionado (o fatiamento truncará seu objeto, mas não fará com que seu programa trave aqui).
Cara,
24
-1 Isso não explica completamente o problema real. O C ++ possui semântica de valor, não semântica de referência como Java, portanto, isso é totalmente esperado. E a "correção" é realmente um exemplo de código C ++ realmente horrível . "Corrigir" problemas inexistentes como esse tipo de fatiamento recorrendo à alocação dinâmica é uma receita para o código de buggy, a perda de memória e o desempenho horrível. Note que não são casos em que o corte é ruim, mas esta resposta failes para apontá-los. Dica: o problema começa se você atribuir através de referências .
Fgp
Você entende que tentar acessar um membro do tipo que não está definido ( Dog::breed) não é um erro relacionado ao SLICING?
Croll
4

Parece-me que fatiar não é um problema, a não ser quando suas próprias classes e programas são mal arquitetados / projetados.

Se eu passar um objeto de subclasse como parâmetro para um método, que aceita um parâmetro do tipo superclasse, certamente eu deveria estar ciente disso e conhecer internamente, o método chamado trabalhará apenas com o objeto da superclasse (também conhecida como classe básica).

Parece-me apenas a expectativa irracional de que fornecer uma subclasse em que uma classe básica é solicitada resultaria, de alguma forma, em resultados específicos da subclasse, causaria o fatiamento como um problema. Seu design é ruim no uso do método ou uma implementação de subclasse ruim. Suponho que geralmente é o resultado de sacrificar um bom design de POO em favor de conveniência ou ganhos de desempenho.

Minok
fonte
3
Mas lembre-se, Minok, de que você NÃO está passando uma referência a esse objeto. Você está passando uma NOVA cópia desse objeto, mas usando a classe base para copiá-lo no processo.
Arafangion
cópia / atribuição protegida na classe base e esse problema foi resolvido.
Cara,
1
Você está certo. A boa prática é usar classes básicas abstratas ou restringir o acesso à cópia / atribuição. No entanto, não é tão fácil identificar quando está lá e é fácil esquecer de cuidar. Chamar métodos virtuais com fatias * pode fazer com que coisas misteriosas aconteçam se você fugir sem uma violação de acesso.
Cara
1
Lembro-me dos meus cursos de programação C ++ na universidade que havia boas práticas permanentes de que, para cada classe que criamos, era necessário escrever construtores padrão, copiar construtores e operadores de atribuição, além de um destruidor. Dessa forma, você certificou-se de que a construção da cópia e similares acontecessem da maneira que você precisava, enquanto escrevia a classe ... em vez disso, mais tarde, com algum comportamento estranho aparecendo.
Minok 25/07
3

OK, vou tentar depois de ler muitas postagens explicando o fatiamento de objetos, mas não como isso se torna problemático.

O cenário vicioso que pode resultar em corrupção de memória é o seguinte:

  • A classe fornece atribuição (acidentalmente, possivelmente gerada pelo compilador) em uma classe base polimórfica.
  • O cliente copia e divide uma instância de uma classe derivada.
  • O cliente chama uma função de membro virtual que acessa o estado fatiado.
Cara
fonte
3

Fatiar significa que os dados adicionados por uma subclasse são descartados quando um objeto da subclasse é passado ou retornado por valor ou de uma função que espera um objeto de classe base.

Explicação: Considere a seguinte declaração de classe:

           class baseclass
          {
                 ...
                 baseclass & operator =(const baseclass&);
                 baseclass(const baseclass&);
          }
          void function( )
          {
                baseclass obj1=m;
                obj1=m;
          }

Como as funções de cópia de classe base não sabem nada sobre a derivada, apenas a parte base da derivada é copiada. Isso geralmente é chamado de corte.

Santosh
fonte
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; 
}
brincadeira
fonte
4
Você se importaria em dar alguns detalhes extras? Como sua resposta difere das respostas já postadas?
Alexis Pigeon
2
Eu acho que mais explicações não seriam ruins.
Looper
-1

quando um objeto de classe derivado é atribuído a um objeto de classe base, atributos adicionais de um objeto de classe derivado são cortados (descartados) no objeto de classe 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
}
Varun Kumar
fonte
-1

Quando um objeto de classe derivada é atribuído ao objeto de classe base, todos os membros do objeto de classe derivada são copiados para o objeto de classe base, exceto os membros que não estão presentes na classe base. Esses membros são cortados pelo compilador. Isso é chamado de Fatiamento de Objetos.

Aqui está um exemplo:

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

Irá gerar:

[Error] 'class Base' has no member named 'd'
[Error] 'class Base' has no member named 'e'
Ghulam Moinul Quadir
fonte
Voto negativo porque esse não é um bom exemplo. Também não funcionaria se, em vez de copiar d para b, você usasse um ponteiro, caso em que d e e ainda existissem, mas o Base não tivesse esses membros. Seu exemplo mostra apenas que você não pode acessar membros que a classe não possui.
Stefan Fabian
-2

Acabei de me deparar com o problema da fatia e desembarcou imediatamente aqui. Então, deixe-me adicionar meus dois centavos a isso.

Vamos dar um exemplo de "código de produção" (ou algo que se aproxima):


Digamos que temos algo que despacha ações. Uma interface do usuário do centro de controle, por exemplo.
Essa interface do usuário precisa obter uma lista de itens que podem ser despachados no momento. Então, definimos uma classe que contém as informações de despacho. Vamos chamá-lo Action. Portanto, um Actiontem algumas variáveis ​​de membro. Por simplicidade, temos apenas 2, sendo a std::string namee a std::function<void()> f. Então ele tem um void activate()que apenas executa of membro.

Portanto, a interface do usuário é std::vector<Action>fornecida. Imagine algumas funções como:

void push_back(Action toAdd);

Agora, estabelecemos a aparência da perspectiva da interface do usuário. Não há problema até agora. Mas outro cara que trabalha nesse projeto decide subitamente que existem ações especializadas que precisam de mais informações noAction objeto. Por que razão alguma vez. Isso também pode ser resolvido com capturas lambda. Este exemplo não é obtido 1-1 do código.

Então o cara deriva Actionpara adicionar seu próprio sabor.
Ele passa um exemplo de sua aula caseira para opush_back mas depois o programa dá errado.

Então o que aconteceu?
Como você pode ter adivinhado: o objeto foi cortado.

As informações extras da instância foram perdidas e fagora estão sujeitas a comportamentos indefinidos.


Espero que este exemplo traga luz para aquelas pessoas que realmente não conseguem imaginar coisas quando falam sobre se Aas Bderivações são derivadas de alguma maneira.

Martin B.
fonte