C++11 Classes and Objects (Polymorphism)

1. Introduction to Polymorphism

① Polymorphism is one of the three major object-oriented features of C++.

② Polymorphism is divided into two categories:

  1. Static polymorphism: Function overloading (the function name has multiple forms) and operator overloading (the symbol has multiple forms) are static polymorphism, and the function name is reused.
  2. Dynamic Polymorphism: Derived classes and virtual functions implement runtime polymorphism.

③ The difference between static polymorphism and dynamic polymorphism:

  1. The function address of static polymorphism is bound early, and the function address is determined during the compilation phase.
  2. The function address of dynamic polymorphism is bound late, and the function address is determined during the running phase.

④ Polymorphism satisfies the conditions:

  1. Inheritance
  2. The subclass overrides the virtual function in the parent class

⑤ Polymorphic usage conditions:

  1. A parent class pointer or reference points to a subclass object

the    void doSpeak(Animal &animal)  // Animal & animal = cat

⑥ Rewriting: The function return value type, function name, and parameter list are all exactly the same, which is called rewriting.

2. Polymorphic address binding

2.1 Address early binding

#include <iostream>
using namespace std;

//polymorphism

//animal
class Animal
{
public:
    void speak()
    {
        cout << "animals are talking" << endl;
    }
};

//cats
class Cat:public Animal
{
public:
    void speak()
    {
        cout << "kitten talking" << endl;
    }
};

//Execute the speaking function
//Address early binding to determine the function address during compilation
//If you want to execute Let the cat talk, then this function cannot be bound in advance, it needs to be bound during the running phase, and the address is bound late
void doSpeak(Animal &animal)  // Animal & animal = cat
{
    animal.speak();
}

void test01()
{
    Cat cat;
    doSpeak(cat);
}

int main()
{
    test01();

    system("pause");

    return 0;

}

operation result:

animals are talking
 Please press any key to continue. . .

2.2 Address late binding virtual function

//Rewriting The function return value type, function name, and parameter list are all exactly the same, which is called rewriting
void speak() //Subclass virtual can be written or not, and can also be written virtual void speak()

#include <iostream>
using namespace std;

//polymorphism

//animal
class Animal
{
public:
    virtual void speak()
    {
        cout << "animals are talking" << endl;
    }
};

//cats
class Cat:public Animal
{
public:
    //Overriding is called rewriting when the return value type, function name, and parameter list of the function are all the same
    void speak()   //The subclass virtual can be written or not, and virtual void speak() can also be written
    {
        cout << "kitten talking" << endl;
    }
};

//dog
class Dog:public Animal
{
public:
    virtual void speak()
    {
        cout << "puppy talking" << endl;
    }
};

//Execute the speaking function
//Address early binding to determine the function address during compilation
//If you want to execute Let the cat talk, then this function cannot be bound in advance, it needs to be bound during the running phase, and the address is bound late

//Dynamic polymorphism satisfies the condition
//1. There is an inheritance relationship
//2. The subclass overrides the virtual function of the parent class

//Dynamic polymorphism uses
//A parent class reference or pointer points to a subclass object

void doSpeak(Animal &animal)  // Animal & animal = cat
{
    animal.speak();
}

void test01()
{
    Cat cat;
    doSpeak(cat);

    Dog dog;
    doSpeak(dog);
}

int main()
{
    test01();

    system("pause");

    return 0;

}
operation result:

kitten talking
 puppy talking
 Please press any key to continue. . .

3. Analysis of the principle of polymorphism

① When no rewriting occurs, the virtual function table in the subclass is the virtual function address of the parent class.

② When the subclass rewrites the virtual function of the parent class, the virtual function table in the subclass will be replaced with the virtual function address of the subclass.  

Only non-static member variables belong to objects of the class

#include <iostream>
using namespace std;

//polymorphism

//animal
class Animal
{
public:                   //If it is a virtual function, then a pointer is stored in the class, and the class occupies 4 bytes
    virtual void speak()  //If it is a non-static member function void speak(), then the function is not on the class, and the empty class occupies 1 byte space                     
    {
        cout << "animals are talking" << endl;
    }
};

//cats
class Cat:public Animal
{
public:

    void speak()   
    {
        cout << "kitten talking" << endl;
    }
};

//dog
class Dog:public Animal
{
public:
    virtual void speak()
    {
        cout << "puppy talking" << endl;
    }
};

//Execute the speaking function
//Address early binding to determine the function address during compilation
//If you want to execute Let the cat talk, then this function cannot be bound in advance, it needs to be bound during the running phase, and the address is bound late

//Dynamic polymorphism satisfies the condition
//1. There is an inheritance relationship
//2. The subclass overrides the virtual function of the parent class

//Dynamic polymorphism uses
//A parent class reference or pointer points to a subclass object

void doSpeak(Animal &animal)  // Animal & animal = cat
{
    animal.speak();
}

void test01()
{
    Cat cat;
    doSpeak(cat);

    Dog dog;
    doSpeak(dog);
}

void test02()
{
    cout << "sizeof Animal = " << sizeof(Animal) << endl;
}

int main()
{
    //test01();
    test02();

    system("pause");

    return 0;

}


operation result:

sizeof Animal = 4
 Please press any key to continue. . .

4. Pure virtual functions and abstract classes

① In polymorphism, usually the implementation of the virtual function in the parent class is meaningless, mainly calling the rewritten content of the subclass. Therefore, virtual functions can be changed to pure virtual functions.

② Pure virtual function syntax: virtual return value type function name (parameter list) = 0;

③ When there is a pure virtual function in the class, this class is also called an abstract class.

④ Abstract class features:

  1. Could not instantiate object
  2. The subclass must rewrite the pure virtual function in the abstract class, otherwise it also belongs to the abstract class.
#include <iostream>
using namespace std;

//Pure virtual functions and abstract classes
class Base
{
public:
    //pure virtual function
    //As long as there is a pure virtual function, this class is called an abstract class
    //Abstract class features:
    //1. The object cannot be instantiated
    //2. The subclass of the abstract class must rewrite the pure virtual function in the parent class, otherwise it also belongs to the abstract class
    virtual void func() = 0;
};

class Son : public Base
{
public:
    virtual void func()
    {
        cout << "func function call" << endl;
     }
};
void test01()
{
    //Base b; //Abstract classes cannot instantiate objects
    //new Base; //Abstract classes cannot instantiate objects

    //Son s; //The pure virtual function in the parent class must be rewritten in the subclass, otherwise the object cannot be instantiated

    Base* base = new Son;
    base->func();
}

int main()
{
    test01();

    system("pause");

    return 0;

}

operation result:

func function call
 Please press any key to continue. . .

5. Virtual destructor and pure virtual destructor

① When using polymorphism, if there are attributes in the subclass that are allocated to the heap area, then the parent class pointer cannot call the destructor code of the subclass when it is released.

② Solution: Change the destructor in the parent class to virtual destructor or pure virtual destructor.

③ The commonality between virtual destructor and pure virtual destructor:

  1. It can solve the parent class pointer to release the subclass object
  2. need to have a specific function implementation

④ Virtual destructor syntax: virtual. class name () {}

⑤ Pure virtual destructor syntax:

  1. virtual~classname = 0;
  2. classname::~classname(){}

① Virtual destructor or pure virtual destructor is used to solve the problem of releasing subclass objects through parent class pointers.

② If there is no heap area data in the subclass, it does not need to be written as virtual destructor or pure virtual destructor.

③ A class with a pure virtual destructor is also an abstract class.

#include <iostream>
using namespace std;
#include<string>

//Pure virtual functions and pure virtual institutions
class Animal 
{
public:
    Animal()
    {
        cout << "Animal constructor call" << endl;
    }

    //pure virtual function
    virtual void speak() = 0;

    /*
    //Using virtual destructor can solve the problem that the parent class pointer is not clean when releasing the subclass object
    virtual ~Animal()
    {
        cout << "Animal Destructor call " << endl;
    }
    */

    //Pure virtual destructor requires both declaration and implementation
    //With pure virtual destructor, this class is also an abstract class, and objects cannot be instantiated
    virtual ~Animal() = 0;
    
};

Animal::~Animal()
{
    cout << "Animal Pure virtual destructor call" << endl;
}


class Cat : public Animal
{
public:
    Cat(string name)
    {
        cout << "Cat constructor call" << endl;
        m_Name = new string(name);
    }

    virtual void speak()
    {
        cout << *m_Name << "kitten talking" << endl;
     }

    ~Cat()
    {
        if (m_Name != NULL)
        {
            cout << "Cat destructor call" << endl;
            delete m_Name;
            m_Name = NULL;
        }
    }

    string* m_Name;
};

void test01()
{
    Animal* animal = new Cat("Tom");
    animal->speak();
    //When the parent class pointer is destructed, the destructor in the subclass will not be called, resulting in memory leaks if the subclass has heap attributes
    delete animal;
}

int main()
{
    test01();

    system("pause");

    return 0;
}

operation result:

Animal constructor call
Cat constructor call
Tom kitten talking
Cat destructor call
Animal Pure virtual destructor call
 Please press any key to continue. . .

6. Calculator

① Case description: Distributed uses common writing and polymorphic technology to design and implement a calculator class that performs operations on two operands.

② Advantages of polymorphism:

  1. The code organization structure is clear.
  2. Much more readable.
  3. It is conducive to the expansion and maintenance of the early and late stages.

③ C++ development advocates the use of polymorphism to design program architecture, because polymorphism has many advantages.

6.1 Common writing method to realize polymorphism

#include <iostream>
using namespace std;
#include<string>

//Ordinary methods implement polymorphism

class Calculator
{
public:
    int getResult(string oper)
    {
        if (oper == "+")
        {
            return m_Num1 + m_Num2;
        }
        else if (oper == "-")
        {
            return m_Num1 - m_Num2;
        }
        else if (oper == "*")
        {
            return m_Num1 * m_Num2;
        }
        //If you want to expand new functions, you need to modify the source code
        //Advocate the principle of opening and closing in real development
        //Open-closed principle: develop for extension, close for modification
    }
    int m_Num1; //operand 1
    int m_Num2; //operand 2
};

void test01()
{
    //Create a calculator object
    Calculator c;
    c.m_Num1 = 10;
    c.m_Num2 = 10;

    cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult("+") << endl;

    cout << c.m_Num1 << "-" << c.m_Num2 << "=" << c.getResult("-") << endl;

    cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getResult("*") << endl;
    
}

int main()
{
    test01();

    system("pause");

    return 0;

}

operation result:

10+10=20
10-10=0
10*10=100
 Please press any key to continue. . .

6.2 Polymorphic Design Program Architecture

#include <iostream>
using namespace std;
#include<string>

//Ordinary methods implement polymorphism

#include <iostream>
using namespace std;

//polymorphism

class AbstractCalculator
{
public:
    virtual int getResult()
    {
        return 0;
    }
    int m_Num1;
    int m_Num2;

};

//Addition calculator class
class AddCalculator : public AbstractCalculator
{
public:
    int getResult()
    {
        return m_Num1 + m_Num2;
    }
};

//Subtraction Calculator Class
class SubCalculator : public AbstractCalculator
{
public:
    int getResult()
    {
        return m_Num1 - m_Num2;
    }
};

//Multiplication Calculator Class
class MulCalculator : public AbstractCalculator
{
public:
    int getResult()
    {
        return m_Num1 * m_Num2;
    }
};

void test02()
{
    //polymorphic use condition
    //The parent class pointer or reference points to the child class object
    AbstractCalculator* abc = new AddCalculator;  //Here the parent class pointer is used to point to the subclass object
    abc->m_Num1 = 100;
    abc->m_Num2 = 200;

    cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult() << endl;
    //Remember to destroy after use
    delete abc;

    //Subtraction
    abc = new SubCalculator;  //pointer is not freed
    abc->m_Num1 = 100;
    abc->m_Num2 = 200;

    cout << abc->m_Num1 << "-" << abc->m_Num2 << "=" << abc->getResult() << endl;
    //Remember to destroy after use
    delete abc;

    //Multiplication
    abc = new MulCalculator;  //pointer is not freed
    abc->m_Num1 = 100;
    abc->m_Num2 = 200;

    cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->getResult() << endl;
    //Remember to destroy after use
    delete abc;
}

int main()
{
    test02();

    system("pause");

    return 0;

}

operation result:

100+200=300
100-200=-100
100*200=20000
 Please press any key to continue. . .

7. Make drinks

① Case description: The general process of making a drink is: boiling water - brewing - pouring into a cup - adding auxiliary materials

② Use polymorphic technology to realize this case, provide an abstract base class for making beverages, and provide subclasses for making coffee and tea.

#include <iostream>
using namespace std;

class AbstractDring
{
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;

    //making drinks
    void makeDrink()
    {
        Boil();
        Brew();
        PourInCup();
        PutSomething();
    }

};

//making coffee
class Coffee :public AbstractDring
{
public:
    //boil water
    virtual void Boil()
    {
        cout << "Boil Nongfu Spring" << endl;
    }

    //brew
    virtual void Brew()
    {
        cout << "brew coffee" << endl;
    }

    //pour into cup
    virtual void PourInCup()
    {
        cout << "pour into cup" << endl;
    }

    //Add accessories
    virtual void PutSomething()
    {
        cout << "add sugar and milk" << endl;
    }
};

//making tea
class Tea :public AbstractDring
{
public:
    //boil water
    virtual void Boil()
    {
        cout << "boil tea" << endl;
    }

    //brew
    virtual void Brew()
    {
        cout << "brewing tea" << endl;
    }

    //pour into cup
    virtual void PourInCup()
    {
        cout << "pour into cup" << endl;
    }

    //Add accessories
    virtual void PutSomething()
    {
        cout << "add goji berries" << endl;
    }
};

//make function
void doWork(AbstractDring* abs)
{
    abs->makeDrink();
    delete abs;  //freed
}

void test01()
{
    //making coffee
    doWork(new Coffee);
    cout << "----------------" << endl;
    doWork(new Tea);
}

int main()
{
    test01();

    system("pause");

    return 0;

}

operation result:

Boil Nongfu Spring
 brew coffee
 pour into cup
 add sugar and milk
 boil tea
 brewing tea
 pour into cup
 add goji berries
 Please press any key to continue. . .

8. Computer Assembly

Case description: The main components of a computer are CPU (for computing), graphics card (for display), memory stick (for storage), and each part is encapsulated into an abstract base class; and different manufacturers are provided to produce different parts , such as intel manufacturers and Lennovo manufacturers; create a computer class to provide functions for computer work, and call the interface of each part work. Three different computers were assembled for work during the test.

#include <iostream>
using namespace std;
#include<string>

//Abstract different part classes
//Abstract CPU class
class CPU
{
public:
    //abstract calculation function
    virtual void calculate() = 0;
};

//Abstract graphics class
class VideoCard
{
public:
    //abstract display function
    virtual void display() = 0;
};

//Abstract Memory Class
class Memory
{
public:
    //abstract storage function
    virtual void storage() = 0;
};

//computer class
class Computer
{
public:
    Computer(CPU* cpu, VideoCard* vc, Memory* mem)
    {
        m_cpu = cpu;
        m_vc = vc;
        m_mem = mem;
    }

    //function that provides work
    void work()
    {
        m_cpu->calculate();
        m_vc->display();
        m_mem->storage();
    }

    //Provide a destructor to release 3 computer parts
    ~Computer()
    {
        if (m_cpu != NULL)
        {
            delete m_cpu;
            m_cpu = NULL;
        }

        if (m_vc != NULL)
        {
            delete m_vc;
            m_vc = NULL;
        }

        if (m_mem != NULL)
        {
            delete m_mem;
            m_mem = NULL;
        }
    }

private:
    CPU* m_cpu; //CPU's Part Pointer
    VideoCard* m_vc; //Graphics Card Parts Pointer
    Memory* m_mem; //Memory stick part pointer
};

//Specific manufacturers
//Intel manufacturers
class IntelCPU : public CPU
{
public:
    virtual void calculate()
    {
        cout << "Intel of CPU Start counting!" << endl;
    }
};

class IntelVideoCard : public VideoCard
{
public:
    virtual void display()
    {
        cout << "Intel The graphics card started counting!" << endl;
    }
};

class IntelMemory : public Memory
{
public:
    virtual void storage()
    {
        cout << "Intel The memory stick started to store!" << endl;
    }
};

//Lenovo manufacturer
class LenovoCPU : public CPU
{
public:
    virtual void calculate()
    {
        cout << "Lenovo of CPU Start counting!" << endl;
    }
};

class LenovoVideoCard : public VideoCard
{
public:
    virtual void display()
    {
        cout << "Lenovo The graphics card started counting!" << endl;
    }
};

class LenovoMemory : public Memory
{
public:
    virtual void storage()
    {
        cout << "Lenovo The memory stick started to store!" << endl;
    }
};

void test01()
{
    //first computer parts
    CPU* intelCpu = new IntelCPU;
    VideoCard* intelCard = new IntelVideoCard;
    Memory* intelMem = new IntelMemory;

    cout << "The first computer starts working:" << endl;
    //Create your first computer
    Computer* computer1 = new Computer(intelCpu, intelCard, intelMem);
    computer1->work();
    delete computer1;

    cout << "---------" << endl;
    cout << "The second computer starts working:" << endl;
    //Create a second computer
    Computer* computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);
    computer2->work();
    delete computer2;

    cout << "---------" << endl;
    cout << "The third computer starts working:" << endl;
    //Create a third computer
    Computer* computer3 = new Computer(new IntelCPU, new LenovoVideoCard, new LenovoMemory);
    computer3->work();
    delete computer3;
}

int main()
{
    test01();

    system("pause");

    return 0;
}


operation result:

The first computer starts working:
Intel of CPU Start counting!
Intel The graphics card started counting!
Intel The memory stick started to store!
The second computer starts working:
Lenovo of CPU Start counting!
Lenovo The graphics card started counting!
Lenovo The memory stick started to store!
The third computer starts working:
Intel of CPU Start counting!
Lenovo The graphics card started counting!
Lenovo The memory stick started to store!
Please press any key to continue. . .

Tags: C++ programming language

Posted by rbeschizza on Sat, 25 Mar 2023 04:28:53 +0530