Technology Sharing

C: Difference between composition and inheritance

2024-07-12

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

Introduction to composition and comparison with inheritance

What is a combination
(1) Composition, which is to use objects of multiple classes as members in one class
(2) Using class tree to explain cases
(3) Combination is also a method of code reuse, and its essence isStructureInclude

#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

insert image description here

Comparison of the characteristics of inheritance and composition
(1) Inheritance is a kind ofis a) The relationship is transitive and not symmetric.
(2) Combination is a part of (has a) relationship,
(3) Inheritance is white-box reuse. Because class inheritance allows us to override and rewrite the implementation details of the parent class according to our own implementation, the parent class's implementation is visible to the child class.
(4) The white-box reuse feature of inheritance destroys the encapsulation feature of the class to a certain extent, because it exposes the implementation details of the parent class to the child class.
(5) Combination is a black box reuse. The internal details of the contained objects are not visible to the outside world, so its encapsulation is relatively good and the mutual dependence in implementation is relatively small.
(6) The included class in the combination will be created when the containing class is created, and will disappear when the containing class disappears. Combination belongs to black box reuse, and can be defined dynamically during runtime by obtaining other object references or pointers of the same type. The disadvantage is that it causes too many objects in the system.
(7) OO design principle is to prioritize composition, then inheritance

Multiple inheritance and its ambiguity

Multiple inheritance
(1) Multiple inheritance means that a subclass has multiple parent classes
(2) Multiple inheritance demonstration
(3) There is no significant difference between the principles of multiple inheritance and single inheritance.
(4) Multiple inheritance can lead to ambiguity
Multiple inheritance ambiguity problem 1
(1) Scenario: C inherits from both A and B. There will be ambiguity when C calls members of A and B with the same name.
(2) Reason: C inherits a member with the same name (in different namespaces) from A and B respectively, so when we call it with the object of C, the compiler cannot determine which one we want to call.
(3) Solution 1: Avoid conflicts by making sure that the public member names of A and B do not overlap. However, this is sometimes uncontrollable.
(4) Solution 2: When coding, specify which one to call. Use cA::func() to specify that the func of class A is called instead of class B.
(5) Solution 3: Redefine func in C. Then, when calling, the func in C will be called, and the ones in A and B will be hidden.
(6) Conclusion: These problems can be solved, but not very well.

Multiple inheritance ambiguity problem 2
(1) Scenario: Diamond inheritance problem. A is the ancestor class, B1:A, B2:A, C:B1,B2. In this case, there will be ambiguity when using C's object to call a method in A.
(2) Analysis: c.func() is ambiguous, cA::func() is also ambiguous, but c.B1::func() and c.B2::func() are not ambiguous.
(3) Solution: Same as in problem 1, but problem 2 is more subtle and more difficult to avoid.

insert image description here

// 祖类
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

Virtual inheritance solves the ambiguity problem of diamond inheritance

How to use virtual inheritance
(1) Scenario: Diamond inheritance leads to ambiguity. Essentially, there are two copies of A objects contained in B1 and B2 in the grandchild class C, which leads to ambiguity.
(2) Virtual inheritance solution: Let B1 and B2 virtually inherit A, and then C can inherit B1 and B2 normally.
(3) Virtual inheritance is as simple as that. It was created to solve the ambiguity problem of diamond inheritance and has no direct relationship with virtual functions (to achieve polymorphism).

The implementation principle of virtual inheritance
(1) The principle of virtual inheritance is: virtual base class table pointer vbptr and virtual base class table virtual table
(2) References:https://blog.csdn.net/xiejingfa/article/details/48028491

insert image description here

Summarize

Understand what is a combination, a class has many member variables of other class types
Understand the difference between composition and inheritance
Ambiguity: A function is executed, but it may not be the one you want to specify
Solution: Use cA::func() to explicitly specify that the class is being called.
Virtual inheritance is a bit like conditional compilation. It only needs to be introduced once, and the introduction effect can be achieved without repeated introduction.

Learning records, please contact us to delete if there is any infringement.
Source: Mr. Zhu's Internet of Things Classroom