Compartilhamento de tecnologia

C: A diferença entre composição e herança

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Introdução à composição e comparação com herança

o que é combinação
(1) Composição significa usar objetos de várias outras classes como membros de uma classe.
(2) Use a árvore de classes para explicar os casos
(3) A combinação também é um método de reutilização de código e sua essência éEstruturaIncluir

#include <iostream>
#include <vector>
#include <string>

// Leaf 类
class Leaf {
public:
    Leaf(const std::string& color) : color_(color) {
        std::cout << "Leaf constructor called: " << color_ << std::endl;
    }

    ~Leaf() {
        std::cout << "Leaf destructor called: " << color_ << std::endl;
    }

    void display() const {
        std::cout << "Leaf color: " << color_ << std::endl;
    }

private:
    std::string color_;
};

// Branch 类
class Branch {
public:
    Branch(int length) : length_(length) {
        std::cout << "Branch constructor called: " << length_ << " cm" << std::endl;
    }

    ~Branch() {
        std::cout << "Branch destructor called: " << length_ << " cm" << std::endl;
    }

    void display() const {
        std::cout << "Branch length: " << length_ << " cm" << std::endl;
    }

private:
    int length_;
};

// Tree 类,包含 Leaf 和 Branch 对象
class Tree {
public:
    Tree(const std::string& leafColor, int branchLength) 
        : leaf_(leafColor), branch_(branchLength) {
        std::cout << "Tree constructor called" << std::endl;
    }

    ~Tree() {
        std::cout << "Tree destructor called" << std::endl;
    }

    void display() const {
        leaf_.display();
        branch_.display();
    }

private:
    Leaf leaf_;
    Branch branch_;
};

int main() {
    Tree tree("Green", 150);
    tree.display();
    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

Insira a descrição da imagem aqui

Comparação das características de herança e composição
(1) Herança é uma espécie de (é a) A relação é transitiva e não simétrica.
(2) A combinação é a relação de uma parte de (tem um),
(3) Herança é reutilização de caixa branca. Como a herança de classe nos permite substituir os detalhes de implementação da classe pai de acordo com nossa própria implementação, a implementação da classe pai fica visível para a classe filha.
(4) O recurso de reutilização de caixa branca da herança destrói o recurso de encapsulamento da classe até certo ponto, porque exporá os detalhes de implementação da classe pai à subclasse.
(5) A combinação pertence à reutilização de caixa preta.Os detalhes internos do objeto contido não são visíveis para o mundo exterior, portanto seu encapsulamento é relativamente bom e a interdependência na implementação é relativamente pequena.
(6) A classe incluída na combinação será criada e destruída à medida que a classe que a contém é criada. As combinações são reutilizadas em caixa preta e podem ser definidas dinamicamente em tempo de execução, obtendo outras referências de objetos ou ponteiros do mesmo tipo. A desvantagem é que existem muitos objetos no sistema.
(7) O princípio de design OO é combinar primeiro e depois herdar.

Herança múltipla e seus problemas de ambigüidade

herança múltipla
(1) Herança múltipla significa que uma subclasse possui várias classes pai
(2)Demonstração de herança múltipla
(3) Não há diferença óbvia entre os princípios da herança múltipla e da herança única.
(4) Herança múltipla pode levar a problemas de ambiguidade
A ambigüidade da herança múltipla 1
(1) Cenário: C tem herança múltipla de A e B, portanto haverá ambigüidade ao chamar membros com os mesmos nomes de A e B em C.
(2) Motivo: C herda um membro com o mesmo nome (domínio de namespace diferente) de A e B, portanto, ao chamar com um objeto de C, o compilador não pode determinar qual deles queremos chamar.
(3) Solução 1: Para evitar a ocorrência, deixe que a nomenclatura dos membros públicos de A e B não entre em conflito repetidamente. Mas isso às vezes é incontrolável.
(4) Solução 2: Especifique claramente qual chamar durante a codificação, use cA::func() para especificar claramente que a função da classe A é chamada em vez da função da classe B.
(5) Solução 3: Redefina func em C, então a função em C será chamada e a função em A e B será ocultada.
(6) Resumo: Pode ser resolvido, mas não existe uma boa solução.

A ambigüidade da herança múltipla 2
(1) Cenário: problema de herança de diamantes.Ou seja, A é a classe ancestral, B1:A, B2:A, C:B1,B2. Neste momento, haverá ambigüidade ao usar o objeto de C para chamar um método em A.
(2) Análise: c.func() é ambíguo, cA::func() também é ambíguo, mas c.B1::func() e c.B2::func() não são ambíguos
(3) Solução: Igual ao problema 1, mas o problema 2 é mais sutil e mais difícil de evitar

Insira a descrição da imagem aqui

// 祖类
class Aa {
 public:
  void show() { std::cout << "A's method" << std::endl; }
};

// 派生类 B1 和 B2,从 A 继承
class B1 : public Aa {
 public:
  void show() { std::cout << "B1's show" << std::endl; }
  void showB1() { std::cout << "B1's method" << std::endl; }
};

class B2 : public Aa {
 public:
  void show() { std::cout << "B2's show" << std::endl; }
  void showB2() { std::cout << "B2's method" << std::endl; }
};

// 派生类 C,从 B1 和 B2 继承
class C : public B1, public B2 {
 public:
  void showC() { std::cout << "C's method" << std::endl; }
};

int test070103() {
  C c;
  // c.Aa::show();    // 调用 A 的方法 不可以
  c.Aa::B1::show();  // 调用 A 的方法 不可以
  c.B2::Aa::show();  // 调用 A 的方法 不可以
  c.B1::show();      // 调用 B1 的方法
  c.B2::show();      // 调用 B2 的方法
  c.showB1();        // 调用 B1 的方法
  c.showB2();        // 调用 B2 的方法
  c.showC();         // 调用 C 的方法
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

A herança virtual resolve o problema de ambiguidade da herança de diamantes

Como usar herança virtual
(1) Cenário: A herança diamante leva a problemas de ambiguidade. Essencialmente, existem dois objetos A contidos em B1 e B2 na classe neta C, portanto, há ambiguidade.
(2) Solução de herança virtual: deixe B1 e B2 herdarem A virtualmente e então C poderá herdar B1 e B2 normalmente.
(3) A herança virtual é simples assim. Ela é criada para resolver o problema de ambiguidade da herança de diamante. Não tem relação direta com funções virtuais (para obter recursos polimórficos).

O princípio de implementação da herança virtual
(1) O princípio da herança virtual é: ponteiro de tabela de classe base virtual vbptr e tabela de classe base virtual tabela virtual
(2)Referência:https://blog.csdn.net/xiejingfa/article/details/48028491

Insira a descrição da imagem aqui

Resumir

Entenda o que é combinação. Existem muitas variáveis ​​de membro de outros tipos de classe em uma classe.
Entenda a diferença entre composição e herança
Ambiguidade: Se você executar uma função, pode não ser necessariamente aquela que você deseja especificar.
Solução: Use cA::func() para especificar explicitamente a classe que está sendo chamada
A herança virtual é um pouco como a compilação condicional. Ela só precisa ser introduzida uma vez. Não precisa ser introduzida repetidamente para obter o efeito de introdução.

Os registros do estudo e os contatos de infração serão excluídos.
Fonte: Sala de aula de Internet das Coisas do professor Zhu