c++: encapsulation, inheritance, polymorphism

Table of Contents

introduction

1. Encapsulation

1. What is packaging?

2. Why encapsulation?

3. Access control modifiers

4. Design and implementation of classes

5. Advantages of packaging

2. Inheritance

1. What is inheritance?

2. Why inherit?

2.1 Inherited permissions diagram

2.2 Single inheritance case

3. Multiple inheritance

3.1 Masonry inheritance

4. Virtual inheritance

4.1 Virtual inheritance implementation

4.2 Principle of virtual inheritance

5. Advantages of inheritance

3. Polymorphism

1. What is polymorphism?

2. Why polymorphism?

3. Virtual functions and pure virtual functions

3.1 Virtual function

3.2 Pure virtual function

3.2.1 Case introduction

4. Virtual destruction and pure virtual destruction

4. 1 Virtual destruction (solve memory leaks)

4.2 Pure virtual destruction

5. Advantages of polymorphism


Introduction

In modern software development, Object Oriented Programming has become a widely used programming paradigm. C++, as a language that supports object-oriented programming, provides powerful features in terms of encapsulation, inheritance, and polymorphism. This article will introduce the concepts of encapsulation, inheritance, and polymorphism in C++ and illustrate their use through simple examples.

1. Encapsulation

1. What is encapsulation

Encapsulation is an important concept in object-oriented programming. It encapsulates data and operations to form a “black box”, which only exposes necessary interfaces to the outside and hides internal implementation details. C++ implements encapsulation through classes, which include access control modifiers such as private members (Private), protected members (Protected), and public members (Public). Private members can only be accessed within the class, protected members can be accessed within the class and its subclasses, and public members can be accessed from anywhere.

First of all, before introducing encapsulation, you should know about classes and objects. What are classes and objects?

Classes and objects are like your object making a wish list (drawn pie) for you. They are all templates, abstract and simulated things. Then one day, your object will realize a specific “pie” for you. “, this pie actually came true. This is somewhat similar.

2. Why should we encapsulate

The cake that your partner draws for you, you know and I know. It would be nice to not let him know that he is not my partner. Each has its own circle (security, maintainability). Then what is encapsulated is some passwords, but it cannot be completely closed like a “closed door” (what is the use of this class of yours? It does not provide a calling interface to the outside world).

3. Access control modifier

within class

in derived class

except both

private

×

×

protected

×

public

Table 1-1 Access control modifiers

This will not be elaborated on.

4. Design and implementation of classes

#include<bits/stdc + + .h>

using namespace std;

class Dog{//Create a Dog object
private:
    string name;//an attribute
public:
    Dog(string name = ""):name(name){}//Initialization construction list (default parameters)
    void eat(string food)//A method
    {
        cout<<"I am "<<name<<", I like eat "<<food<<endl;
    }
};

int main()
{
    Dog dog1("woof"); //Instance an object
    dog1.eat("bone");//Call member method
    Dog dog2("Wangcai"); //Instance an object
    dog2.eat("poop");//Call member method
// cout<<dog1.name<<endl; //error is private and cannot be accessed outside the class
    return 0;
}
Figure 1-1 Implementation of classes and objects

When you read this code, do you feel that you understand a little bit? Dog is a class, representing the dog class, and actual dogs are all instances of the dog class.

5. Advantages of encapsulation

Encapsulation has the advantages of improving maintainability, increasing reusability, providing security and reducing coupling (to achieve high cohesion and low coupling), and is suitable for class and object design, interface design, data encapsulation and access control , as well as the development of class libraries and frameworks. By properly applying encapsulation, you can write code that is more robust, flexible, and easier to maintain.

2. Inheritance

1. What is inheritance

In an inheritance relationship, a subclass can obtain the properties and methods of the parent class, and can add its own specific functions or modify the behavior of the parent class on this basis. A subclass can inherit the public members (public) of the parent class, but it cannot inherit the private members (private). Private members can only be accessed within the parent class. What is inherited is the commonality that everyone has. I add, change, and make it my own personality.

2-1 Inheritance icon

2. Why inheritance

Figure 2-2 Inheritance scenario
  1. Code reuse: Through inheritance, subclasses can directly use the properties and methods already defined by the parent class, avoiding repeatedly writing the same code and improving code reusability.

  2. Organize hierarchical relationships of classes: Through inheritance, classes can be organized into hierarchical structures to form relationships between parent classes and subclasses. This allows for better organization and management of code, making the code structure clearer and easier to understand.

  3. Polymorphism: Inheritance is the basis for achieving polymorphism. Through inheritance, subclasses can override the methods of the parent class and implement different behaviors. In this way, when using variables of the parent class type, the corresponding method can be called according to the actual object type to implement different processing logic.

2, 1 Inherited Permission Map

Figure 2-3 Inherited permissions diagram

2, 2 single inheritance case

#include<bits/stdc + + .h>

using namespace std;

class Base{
private:
    int a=0;
protected:
    int b=0;
public:
    int c=0;
};
class Son :public Base{
    //Protected b public c can be accessed in subclasses
public:
    void func(void)
    {
        cout<<b<<c<<endl;
        //cout<<a<<end1;//Inaccessible
    }
};
int main(){
    Son ob;
    //cout<<ob.b<<end1;//Cannot be accessed outside the class
    cout<<ob.c<<endl;
    ob.func();
    return 0;
}
Figure 2-4 Single inheritance permission character case

3. Multiple inheritance

Multiple inheritance, as the name suggests: a class has two or more parent classes. Then there will be cases where member variables and member functions have the same name. In this case, they should be hidden. A good all-purpose method is to add scope qualifiers.

#include<bits/stdc + + .h>

using namespace std;

class Base1{
public:
    int a;
    Base1(int a):a(a){}
};
class Base2{
public:
    int a;
    Base2(int a) :a(a){}
};
class Son :public Base1,public Base2{
public:
    int a;
    Son(int a,int b,int c): Base1(a),Base2(b),a(c){}
};
int main(){
    Son ob(10,20,30);
    cout<<ob.a<<endl;//Subclass a
    cout<<ob.Base1::a<<endl; //Base1 a
    cout<<ob.Base2::a<<endl;//Base2 a
    return 0;
}

Figure 2-5 Multiple inheritance case

3, 1 masonry inheritance

Diamond inheritance (masonry inheritance): Inheritance with common ancestors is called diamond inheritance.
The lowest subclass data will contain multiple copies (data of common ancestors)

Figure 2-6 Masonry inheritance diagram
#include<bits/stdc + + .h>

using namespace std;

class Animal
{
public:
    int data;
};
class sheep :public Animal{
};
class Tuo :public Animal {};
class sheepTuo:public sheep, public Tuo{};
int main()
{
    sheepTuo ob;
    memset( & amp;ob,0,sizeof(sheepTuo));
    cout<<sizeof(ob);//8, two pieces of data, two ints
    //cout << ob.data << endl;//Ambiguity, I don’t know whose data to call
    cout << ob.sheep:: data << endl;//To resolve the ambiguity, you can still add a scope
    cout << ob. Tuo::data << endl;//To resolve the ambiguity, you can still add a scope
    return 0;
}

4. Virtual inheritance

So as just mentioned, I inherited multiple data. So can I only inherit one share? Then there will be no ambiguity. Simply add virutal in front of the inheritance permission symbol.

4.1 Virtual inheritance implementation

#include<bits/stdc + + .h>

using namespace std;

class Animal
{
public:
    int data;
};
class sheep :virtual public Animal{
};
class Tuo :virtual public Animal {};
class sheepTuo:public sheep, public Tuo{};
int main()
{
    sheepTuo ob;
    memset( & amp;ob,0,sizeof(sheepTuo));
    cout<<sizeof(ob)<<endl;
    cout << ob.data << endl;//resolves the ambiguity
    cout << ob.sheep:: data << endl;
    cout << ob. Tuo::data << endl;
    return 0;
}


Figure 2-7 Implementation of virtual inheritance

4.2 Virtual Inheritance Principle

This can be viewed by using special commands under Vmware to view the layout distribution of classes. (See how the compiler handles class layout)

Figure 2-8 Animal layout
Figure 2-9 Sheep distribution
Figure 2-10 Tuo distribution
Figure 2-11 sheepTuo layout

It can be found that the byte size of sheepTuo after using virtual inheritance is 12. There are two virtual base class pointers respectively, and they point to different virtual base class tables. Because of the different offsets, there will be no ambiguity, and the data can be accessed. There is only one copy of data data.

Figure 2-12 Interpretation of virtual inheritance

5. Advantages of inheritance

In short, you directly inherit from others and use it, which is convenient and efficient. And you can show your own “personality”. It is also very reasonable, but you should also pay attention to some special situations, such as the masonry inheritance mentioned above. The solution introduces directly adding scope qualifiers or virtual inheritance. At the same time, this also pave the way for the subsequent expansion of polymorphism.

Three, polymorphism

1. What is polymorphism

Polymorphism is an important concept in object-oriented programming, which refers to objects of the same type showing different behaviors in different situations. To put it simply, the same method can have different implementations on different objects. If you don’t understand, it doesn’t matter. Detailed introduction will be given below.

2. Why polymorphism

Let me give you a simple example first: the client (landlord) asks you to match the locks for a building with many rooms.

Programmer 1: There are 120 rooms, plus one for the door, and 121 keys for you. They also thoughtfully label you to identify the room number.

Programmer 2: I will give you a master key directly. The landlord can open all doors with one lock.

If it were you, who would you like to take over this project?

3. Virtual function and pure virtual function

Before introducing (pure) virtual functions, a little appetizer.

Figure 3-x Introduction of virtual functions

What this picture wants to convey is how to operate the different parts of each subclass through the pointer or reference of the parent class? Even the subclasses that are coming in the future.

#include<bits/stdc + + .h>

using namespace std;

class Animal{
public:
    void speak (void){
        cout<<"Animals are talking"<<endl;
    }
};
class Dog:public Animal{
public:
    void speak (void){
        cout<<"The dog is barking"<<endl;
    }
};
int main(){
    Animal *p = new Dog;
    p->speak() ;//The animals are talking, but it’s not the dogs that are barking.
                //This is not the result I want. I want to call the method of the derived class through the pointer of the base class.
}


Figure 3-1 Situation where polymorphism is not implemented

3, 1 virtual function

//Polymorphic conditions: There is inheritance, the subclass overrides the virtual function of the parent class, and the parent class pointer points to the subclass space.

#include<bits/stdc + + .h>

using namespace std;

class Animal{
public:
    virtual void speak (void){//virtual function
        cout<<"Animals are talking"<<endl;
    }
};
class Dog:public Animal{
public:
    virtual void speak (void){//Subclasses override fictional functions
        cout<<"The dog is barking"<<endl;
    }
};
class Cat:public Animal{
public:
    virtual void speak (void){//Subclasses override fictional functions
        cout<<"The cat is heehee"<<endl;
    }
};
int main(){
    Animal *p = new Dog;
    p->speak();
    Animal *q = new Cat;
    q->speak();
    return 0;
}
//Simply add a virtual to implement the base class pointer to call the derived class method.

Figure 3-2 Implementing polymorphism

Given the layout distribution:

Figure 3-3 Base class with virtual functions
Figure 3-4 Dog rewrites virtual functions

When Dog inherits Animal, Dog will inherit the virtual function pointer and virtual function table, and when Dog rewrites the virtual function of the parent class, the virtual function address becomes Dog’s function address. (Figure 3-1 —-> Figure 3-2 Implementation principle)

Figure 3-5 Virtual function implementation principle

3, 2 pure virtual functions

The virtual function of the parent class is useless if it is defined. Our intention is not to use it anyway. Then we can just declare it without defining it?

class Animal{
public:
    virtual void speak (void) = 0;//Pure virtual function
};//This class becomes an abstract class and cannot instantiate objects
3, 2, 1 case introduction
Figure 3-6 Case introduction

#include<bits/stdc + + .h>

using namespace std;

//Abstract drink making
class AbstractDrinking{
public:
    //boil water
    virtual void Boil() = 0;
    //Brew
    virtual void Brew() = 0;
    //Pour into cup
    virtual void PourInCup() = 0;
    //Add accessories
    virtual void PutSomething() = 0;
    //Specify the process
    void MakeDrink()
    {
        this->Boil();
        Brew();
        PourInCup();
        PutSomething();
    }
};


//make coffee
class Coffee : public AbstractDrinking
{
public:
    //boil water
    virtual void Boil(){
        cout << "Boil Nongfu Spring!" << endl;
        //Brew
    }
    virtual void Brew(){
        cout << "Make coffee!" << endl;
    }
    //Pour into cup
    virtual void PourInCup(){
        cout << "Pour the coffee into the cup!"<< endl;
    }
    //Add accessories
    virtual void PutSomething(){
        cout << "Add milk!"<< endl;
    }
};


class Tea : public AbstractDrinking
{
public:
    //boil water
    virtual void Boil(){
        cout << "Cook the farmer's three springs!" << endl;
        //Brew
    }
    virtual void Brew(){
        cout << "Make tea!" << endl;
    }
    //Pour into cup
    virtual void PourInCup(){
        cout << "Pour the tea into the pot!"<< endl;
    }
    //Add accessories
    virtual void PutSomething(){
        cout << "Add lemon slices!"<< endl;
    }
};


//Business function
void DoBussiness(AbstractDrinking* drink)
{
    drink->MakeDrink();
    delete drink;
}

int main(){
    DoBussiness(new Coffee);//Call to make coffee
    cout<<"---------------\
";
    DoBussiness(new Tea);//Call to make tea
    return 0;
}
//Do you find it very convenient? Just give you an interface
//As for what to do after adjusting this interface, you decide by yourself.
//Does it look like a template?


Figure 3-7 Implementing the interface

Regarding the implementation of the above case: Is it a bit like a master key? The parent class only needs to provide an interface to call, instead of using different pointers to point to different objects for access, which is also convenient for access. And when you add a new floor portal, you can inherit the same template and implement different functions yourself.

4. Virtual destruction and pure virtual destruction

4, 1 virtual destructor (solve memory leak)

#include<bits/stdc + + .h>

using namespace std;

class Animal
{
public:
    Animal(){
        cout<<"Animal's constructor is called"<<endl;
    }
    ~Animal()
    {
        cout << "Animal's destructor was called" <<endl;
    }
};

class Dog: public Animal
{
public:
    Dog(){
        cout<<"Dog's constructor is called"<<endl;
    }
    ~Dog()
    {
        cout << "Dog's destructor was called" <<endl;
    }
};


int main()
{
    Animal *al = new Dog; //Memory leak
    delete al;
    return 0;
}


Figure 3-8 Destructor (memory leak)

Solution: Add virtual as well. Principle of virtual destruction: release the entire subclass space through the parent class pointer

Figure 3-9 Virtual destructor

The principle of virtual destructor implementation is similar to that of virtual functions. Pointers and tables are also inherited, but the previous address is not overwritten.

Figure 3-10 Implementation principle of virtual destruction

4, 2 pure virtual destruction

The essence of pure virtual destruction:

  • It is the destructor that handles the recycling of each class. And destructors cannot be inherited.
  • A function body must be provided for a pure virtual destructor.
  • Pure virtual destructors must be implemented outside the class.
#include<bits/stdc + + .h>
using namespace std;
class Animal
{
public:
    Animal(){
        cout<<"Animal's constructor is called"<<endl;
    }
    virtual ~Animal() = 0; //Inner class declaration
};

Animal::~Animal()//internal and external definitions
{
    cout << "Animal's destructor was called" <<endl;
}
class Dog: public Animal
{
public:
    Dog(){
        cout<<"Dog's constructor is called"<<endl;
    }
    ~Dog()//It doesn't matter if you write it or not, virtual virtual has "hereditary", and one generation of virtual will replace the virtual.
    {
        cout << "Dog's destructor was called" <<endl;
    }
};
int main()
{
    Animal *al = new Dog;
    delete al;
    return 0;
}
//The implementation effect is exactly the same as that of virtual destructor, except for some changes in the writing method.


5. Advantages of polymorphism

  1. Code reuse: By encapsulating some common codes in parent classes, derived classes can directly inherit these codes, thereby achieving code reuse. This can greatly reduce the amount of code and improve development efficiency.

  2. Flexibility: Using polymorphism can make a program more flexible. Specifically, it can dynamically select different implementation methods at runtime, which allows the program to perform different operations according to different situations.

  3. Scalability: Polymorphism can easily extend program functionality. When you need to add new functions, you only need to create a new subclass and rewrite the corresponding virtual functions without modifying the original code.

  4. Maintainability: Polymorphism can make programs easier to maintain. Due to the advantages of code reuse and flexibility, programs become more concise and clear, easy to understand and modify.

  5. Code readability: Polymorphism can make code more readable. Due to the clear structure of the code, each class only contains its own data and methods

Hope the above content is helpful to you! Don’t forget to hit three in a row with one click! ! !

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. C Skill Tree Home Page Overview 194357 people are learning the system