C + + learning notes: classes and objects

Posted by spamoom on Tue, 23 Nov 2021 04:35:41 +0100


This blog post is a note taken when learning the C + + video of dark horse programmer. It's just convenient to review the old and know the new. It's not used for other purposes.

C + + has three major object-oriented features: encapsulation, inheritance and polymorphism.

1, Encapsulation

Encapsulation: hide the attributes and implementation details of the object, only expose the interface and object for interaction, and organically combine the data with the methods of operating the data.
Meaning of encapsulation:
(1) Take attributes and behaviors as a whole to express things in life
(2) Control attributes and behaviors with permissions

1.1 definition of class

Syntax: class class name {access rights: properties and behaviors}.

#include<iostream>
using namespace std;

//Create class
class Student{
public:
//    attribute
    string name;
    int s_id;
//    behavior
    void ShowStudent()
    {
        cout<<"name:"<<name<<" student_id="<<s_id<<endl;
    }
};

int main()
{
//    Instantiate an object (create an object through a class)
    Student s1;
    s1.name = "Zhang San";
    s1.s_id = 001;
    s1.ShowStudent();
    return 0;
}


1.2 access rights

Attributes and behaviors are controlled under different permissions.
(1) Public: public permission - members can be accessed inside the class and outside the class.
(2) Protected: protected permission - members can access within the class, but not outside the class. Sons can access the protected content in their fathers.
(3) Private: private permission - members can be accessed within the class, but not outside the class.

#include<iostream>
#include<string>
using namespace std;
//Three access rights
//(1) Public: public permission - members can be accessed inside the class and outside the class.
//(2) Protected: protected permission - members can access within the class, but not outside the class. Sons can access the protected content in their fathers.
//(3) Private: private permission - members can be accessed within the class, but not outside the class.
class Person
{
//    Public authority
public:
    string m_name; //name
//    Protection Authority
protected:
    string m_car;//vehicle
//    Private rights
private:
    int m_password;//Payment password
    
public:
    void func()
    {
        m_name="Zhang San";
        m_car = "Trolley";
        m_password=123456;
        cout<<m_name<<m_car<<m_password<<endl;
    }
};
int main()
{
    Person p1;
    p1.m_name="Wang Er Ma Zi";//Public permission, which can also be accessed outside the class
//     p1.m_car = "electric vehicle"// Protection permission. It cannot be accessed outside the class, and its subclasses can be accessed.
//     p1.m_password =987654;// Private property, not accessible outside the class
    p1.func();
    return 0;
}

1.3 differences between class and struct

Can be accessed within the class; Class is private by default and struct is public by default

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

class C1{
    int m_A;//class the default permission is private
};

struct S1{
    int m_A;//The default permission of struct is public
};

int main()
{
    C1 c1;
//    c1.m_A = 100;// report errors
    S1 s1;
    s1.m_A = 100;//normal
    return 0;
}

1.4 privatization of member attributes

advantage:
(1) You can control read and write permissions
(2) For write, the validity of data can be detected

In the example, all variables are set to private, and then corresponding functions are added in public to realize various permissions.

#include<iostream>
#include<string>
using namespace std;
//Achieve goals:
//1. The name is readable and writable
//2. Age read only
//3. Lovers only write
class Person
{
//Step 1: set all variables to private
private:
    string m_name;
    int m_age;
    string m_lover;
//Step 2: add corresponding functions to realize various permissions
public:
//    1. The name is readable and writable
//    Set name (write)
    void setname(string name)
    {
        m_name=name;
    }
//    Display name (read)
    string showname()
    {
        return m_name;
    }

//    2. Age read only
    int readAge()
    {
        m_age=23;
        return m_age;
    }

//    3. Set write only
    void setlover(string lover)
    {
        m_lover = lover;
    }
};

int main()
{
    Person p1;
//    1 name readable and writable
    p1.setname("Wang Wu");
    cout<<p1.showname()<<endl;

//    2 age read only
    cout<<p1.readAge()<<endl;
    
//   3 lovers only write
    p1.setlover("ccc");
}

2, Object properties

2.1 constructors and destructors

Constructor: used to assign a value to the member attribute of the object when creating the object. The constructor is automatically called by the compiler.
Syntax: class name () {}
(1) No return value and no void is written;
(2) The function name is the same as the class name;
(3) Constructors can have parameters, so overloading can occur;
(4) When an object is created, the constructor is called automatically, without manual call, and only once.

Destructor: automatically called by the system before object destruction to perform some cleaning work.
Syntax: ~ class name () {}
(1) No return value and no void is written;
(2) The function name is the same as the class name, and the symbol ~;
(3) Constructors cannot have parameters, so they cannot be overloaded;
(4) Before the object is destroyed, the constructor will be called automatically without manual call, and will only be called once.

#include<iostream>
#include<string>
using namespace std;
//1. Constructors are divided into:
//   By parameter: nonparametric structure, parametric structure
//   By type: normal construction, copy construction
class Person
{
//Constructor
private:
    string m_name;
public:
    Person()//Nonparametric structure
    {
        cout<<"non-parameter constructor !"<<endl;
    }
    Person(int a)//Parametric structure
    {
        m_name=a;
        cout<<"Parameterized constructor!"<<endl;
    }
    Person(const Person &p)//copy constructor 
    {
        cout<<"copy constructor !"<<endl;
    }
//2 destructor
    ~Person()
    {
        cout<<"Destructor!"<<endl;
    }
};
//Call function
void test()
{
    //1. Bracket method
    Person p1;//Default call
    Person p2(10);//Parameterized constructor
    Person p3(p2);//copy constructor 
//    //2. Display method
//    Person p1;
//    Person p2=Person(10);// Parameterized constructor
//    Person p3=Person(p2);// copy constructor 
//    //3. Implicit transformation method
//    Person p4=10;// Equivalent to: Person p4=Person(10), with parameter constructor
//    Person p5=p4;// copy constructor 
}
int main()
{
    test();
    return 0;
}

Difference between deep copy and shallow copy
Deep copy: re apply for space in the heap area for copy operation;
Shallow copy: a simple assignment copy operation.

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

class Person
{
public:
    //non-parameter constructor 
    Person()
    {
        cout << "non-parameter constructor " << endl;
    }

    //Parameterized constructor
    Person(int age ,int height)
    {
        cout << "Parameterized constructor" << endl;
        m_age = age;
        m_height = new int(height);
    }

    //copy constructor 
    Person(const Person& p)
    {
        cout << "copy constructor " << endl;
        //If you do not use deep copy to create new memory in the heap, it will lead to the problem of repeatedly freeing the heap caused by shallow copy
        m_age = p.m_age;
        //m_height = p.m_height;   Copy constructs created by the compiler by default
        m_height = new int(*p.m_height);//Deep copy solution
    }

    //Destructor
    ~Person() {
        if (m_height != NULL)
        {
            delete m_height;
            m_height = NULL;
        }
        cout<<"Destructor call"<<endl;
    }
public:
    int m_age;
    int* m_height;
};

void test()
{
    Person p1(26, 172);

    Person p2(p1);

    cout << "p1 Age of= " << p1.m_age << " height= " << *p1.m_height << endl;

    cout << "p2 Age of=" << p2.m_age << " height= " << *p2.m_height << endl;
}

int main() {
    test();
    return 0;
}

2.2 initialization list

Syntax: constructor (): Property 1, property 2... {}

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

class Person
{
public:
    Person(int a,int b,int c):m_A(a),m_B(b),m_C(c) {}
    void PrintPerson()
    {
        cout << "m_A= " <<m_A <<endl;
        cout << "m_B= " <<m_B <<endl;
        cout << "m_C= " <<m_C <<endl;
    }

private:
    int m_A;
    int m_B;
    int m_C;
};

int main() {
    Person p(1,2,3);
    p.PrintPerson();
    return 0;
}

2.3 class objects as class members

A member in a class can be an object of another class.

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

//Mobile phone class
class Phone
{
public:
    Phone(string Pname)
    {
        m_Pame = Pname;
        cout<<"Phone class"<<endl;
    }
    ~Phone(){
        cout<<"Phone Deconstruction"<<endl;
    }
    string m_Pame;
};
//human beings
class Person
{
public:
    Person(string name,string pName):m_Name(name),m_Phone(pName){
        cout<<"Person class"<<endl;
    }
    ~Person(){
        cout<<"Person Deconstruction"<<endl;
    }
//    full name
    string m_Name;
//    mobile phone
    Phone m_Phone;


};

void test()
{
//  Will construct the class first, and then construct itself;
//  First destruct itself, and then destruct other classes
    Person p("Zhang San","xiaomi");
    cout<<p.m_Name<<"has a "<<p.m_Phone.m_Pame<<endl;
}

int main()
{
    test();
    return 0;
}

2.4 static member function

All objects share a function
Static member functions can only access static member variables

#include<iostream>
using namespace std;
//Create class
class Student{
public:
//Static member function
    static void func()
    {
        m_A = 100;//Static member variables are shared
//Static member functions cannot access non static members because static functions belong to classes rather than to the entire object.
//        m_B = 200;// An error is reported. Static functions cannot access non static member variables. A class can instantiate multiple objects, and the function body cannot distinguish whether it belongs to M_ Which object does B belong to
        cout<<"static void func:"<<endl;
    }
    static int m_A;//Static member variable
    int m_B;//Static member variable
};
int Student::m_A;//Static member variables need to be declared externally

//Two access modes
void test01(){
//   1. Access by object
    Student s1;
    s1.func();

//    2. Access by class name
    Student::func();
}

int main()
{
    test01();
    return 0;
}

2.5 C + + object model and this pointer

2.5.1 member variables and member functions are stored separately

#include<iostream>
#include<string>
using namespace std;
class Person
{
    int m_name;//Non static members belong to class objects and occupy memory
    static int m_b; //Static member variables do not belong to class objects. When accessing memory, it is found that they do not occupy memory
    void func(){}//Non static member functions do not occupy memory and do not belong to objects of classes

};
void test01()
{
    Person p;
//    Storage space occupied by empty objects: 1 byte
    cout<<"the size of p is:"<<sizeof(p)<<endl;

}
int main()
{
    test01();
    return 0;
}

2.5.2 this pointer concept

1 concept: this pointer points to the object to which the called member function belongs.
2. Features:
(1) this pointer is implicit in every non static member function,
(2) this pointer needs no definition and can be used directly
3 Purpose:
(1) When a formal parameter has the same name as a member variable, it can be distinguished by this pointer;
(2) Return the object itself return *this

#include<iostream>
#include<string>
using namespace std;
//The this pointer is implied in every non static member function and is used directly without definition
//Role: 1. Resolve name conflicts; 2. Return the object itself (* this)
class Person
{
public:
    Person(int age)
    {
        //The this pointer points to the object to which the called member function belongs
        this->age = age;
    }
    Person& PersonAddAge(Person &p)
    {
        this->age += p.age;
        return *this;
    }
    int age;
};
void test01()
{
    Person p1(18);
    cout<<"p1 Age of"<<p1.age<<endl;
}
void test02()
{
    Person p1(10);
    Person p2(10);
//    Chain programming idea
    p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);

    cout<<"p2 Age of"<<p2.age<<endl;
}
int main()
{
//    test01();
    test02();
    return 0;
}

2.5.3 null pointer accessing member function (solve by determining whether this pointer is null)

Null pointer calls ordinary member functions. If this pointer is used (access to non static member variables), the program will crash; If the this pointer is not used, the program will not crash.

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

class Person
{
public:
    void showClassName(){
        cout<<"this is Person class"<<endl;
    }
    void showClassage(){
/*+++++++++++++++++Solution: null pointer determination+++++++++++*/
    if(this == NULL){
        return;
    }
/*+++++++++++++++++++++++++++++++++*/
        cout<<"this is class age"<< this->m_age<<endl;
    }
    int m_age;
};

void test01()
{
    Person *p1  = NULL;
    p1->showClassName();
//    If it is not determined whether the this pointer is null, the following line of code will report an error
    p1->showClassage();//Error reason: the passed pointer is null
}

int main()
{
    test01();
    return 0;
}

2.5.4 const modified member functions (constant functions and constant objects)

1. Constant function (void func() const {...})
(1) Add const to the member function to become a constant function;
(2) Member properties cannot be modified in a constant function;
(3) When the member attribute is declared, add the keyword mutable, which can be modified in the constant function.
2. Constant object
(1) When declaring an object, add const before it to become a constant object;
(2) Constant objects can only access constant functions, and ordinary functions cannot be accessed.

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

class Person
{
public:
//    const object 
    Person(){
        m_a = 0;
        m_B = 0;
    }
//    The essence of this pointer is a constant pointer. The pointer cannot be modified
//    Constant function: const is added after the member function to modify the point of this. The value pointed by the pointer cannot be modified
    void showPerson() const
    {
//        this->m_ a = 100;// After const is added, the value pointed to by the pointer cannot be modified. An error will be reported here!
        this->m_B = 100;
        cout<<"Constant function"<<endl;
    }

    void func1()
    {
        cout<<"Ordinary function"<<endl;
    }
public:
    int m_a;
    mutable int m_B;//For special variables, this value can be modified even in constant functions
};

void test01()
{
    Person p1;
//    Class objects can call constant functions and ordinary functions
    p1.showPerson();
    p1.func1();
//    You can also modify member variables, such as member variables with mutabe keyword
    p1.m_a = 100;
    p1.m_B = 100;
}
void test02()
{
//    2 constant object: after const is added to the object, it becomes a constant object, and its value cannot be modified
    const Person p2;
    p2.showPerson();
//    p1.func1();// An error is reported. A constant object can only call a constant function
//    p1.m_a = 100;// Will report an error
    p2.m_B = 100;//The mutabe keyword can be modified

}
int main()
{
    test01();
    test02();
    return 0;
}

3, C + + friends

The function of a friend is to enable a global function or class to access another class.

3.1 global functions as friends

In the example, void goodgay (building * building) is a global function, and friend void goodgay (building * building) is a friend. You can access private members in building.

#include<iostream>
#include<string>
using namespace std;
class Buiding
{
    //goodGay global function is a good friend of Buiding. You can access private members and friends in Buiding
    friend void goodGay(Buiding *buiding);
public:
    Buiding()
    {
        m_sintingroom="a living room";
        m_bedroom = "bedroom";
    }
public:
    string m_sintingroom;//a living room
private:
    string m_bedroom;//bedroom
};
//Global function
void goodGay(Buiding *buiding)
{
    cout<<"goodGay Visiting:"<<buiding->m_sintingroom<<endl;
    cout<<"goodGay Visiting:"<<buiding->m_bedroom<<endl;
}

void test01()
{
    Buiding Buiding;
    goodGay(&Buiding);

}
int main()
{
    test01();
    return 0;
}

3.2 making friends

In the example, add friend class GoodGay in the Building class; In this sentence, the implementation class is a friend, and the GoodGay class can access the private content in the Building class.

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

//Class as friend
class Building;
class GoodGay
{
public:
    GoodGay();
public:
    void visit();//Visiting function 	 Accessing properties in Building
    Building* building;
};

class Building
{
    //goodGay class is a good friend of Building. You can access private members in Building
    friend class GoodGay;//core
public:
    Building();
public:
    string m_SittingRoom;//a living room
private:
    string m_BedRoom;//bedroom
};

//Class write out member function
Building::Building()
{
    m_SittingRoom = "a living room";
    m_BedRoom = "bedroom";
}

GoodGay::GoodGay()
{
    //Create a building object
    building = new Building;
}

void GoodGay::visit()
{
    cout << "Good friends are visiting:" << building->m_SittingRoom << endl;
    cout << "Good friends are visiting:" << building->m_BedRoom << endl;
}

void test01()
{
    GoodGay g;
    g.visit();
}

int main()
{
    test01();
    return 0;
}

3.3 member functions as friends

In the example, the sentence friend void goodGay::visit1() enables the base friend visit1 to access the bedroom and living room, while the base friend visit2() can only access the living room.

//Member function as friend function:
#include<iostream>
#include<string>
using namespace std;

class Building;
class goodGay
{
public:
    goodGay();
    void visit1();//Let visit1 function access private members in Building
    void visit2();//Make the visit2 function inaccessible to private members in Building

private:
    Building * building;
};

class Building
{
//Tell the compiler that the visit1 function under the goodGay class is a good friend of the Building class and can access the private content of the Building class
    friend void goodGay::visit1();
public:
    Building();
public:
    string m_sittingroom;	//a living room
private:
    string m_bedroom;		//bedroom
};

//Member functions implemented outside the class
Building::Building()//Constructor for Building
{
    this->m_bedroom = "bedroom";
    this->m_sittingroom = "a living room";
}

goodGay::goodGay()//Constructor for goodGay
{
    building = new Building;
}
void goodGay::visit1()
{
    cout << "Jiyou visit1 Is" << this->building->m_sittingroom << endl;
    cout << "Jiyou visit1 Is" << this->building->m_bedroom << endl;

}
void goodGay::visit2()
{
    cout << "Jiyou visit2 Is" << this->building->m_sittingroom << endl;
    //Cout < < this - > building - > m_ bedroom << endl;
}
void test01()
{
    goodGay gg;
    gg.visit1();
    gg.visit2();
}

int main()
{
    test01();
    return 0;
}

4, Operator overloading

Concept: redefine the existing operators and give them another function to adapt to different data types.

4.1 overloading of plus operator

Function: realize the operation of adding two user-defined data types.
It mainly includes:
(1) Overload number of member function: Person p3 = p1.operator+(p2);
(2) Global function overload number: Person p3 = operator+(p1,p2);

#include<iostream>
#include<string>
using namespace std;
//Plus operator overload
class Person{

public:
    //1 member function overload + sign
//    Person operator+(Person &p)
//    {
//        Person temp;
//        temp.m_A = this->m_A + p.m_A;
//        temp.m_B = this->m_B + p.m_B;
//        return temp;
//    }
    int m_A;
    int m_B;
};

//2 global function overload + sign
Person operator+(Person &p1,Person &p2)
{
    Person temp;
    temp.m_A = p1.m_A + p2.m_A;
    temp.m_B = p2.m_B + p2.m_B;
    return temp;
}

//Version of function overload
Person operator+(Person &p1,int num)
{
    Person temp;
    temp.m_A = p1.m_A + num;
    temp.m_B = p1.m_B + num;
    return temp;
}

void test01(){
    Person p1;
    p1.m_A = 10;
    p1.m_B = 10;
    Person p2;
    p2.m_A = 10;
    p2.m_B = 10;
//  (1) Simple version call
//    Person p3 = p1 + p2;
//  (2) Essential call of member function
//    Person p3 = p1.operator+(p2);
//  (3) Essential call of global function
    Person p3 = operator+(p1,p2);
//   (4) Function overload version call
    Person p4 = p1 + 100;

    cout<<"p3.m_A = "<<p3.m_A<<endl;//20
    cout<<"p3.m_B = "<<p3.m_B<<endl;//20

    cout<<"p4.m_A = "<<p4.m_A<<endl;//110
    cout<<"p4.m_B = "<<p4.m_B<<endl;//110
}

int main()
{
    test01();
    return 0;
}

4.2 shift left operator overload

Function: output custom data types.

#include"iostream"
using namespace std;
class Person//Create a class yourself
{
//    Declare as friend
    friend ostream & operator<<(ostream &out,Person &p);
public:
    Person(int a,int b)
    {
        this->m_A = a;
        this->m_B = b;
    }

private:
    int m_A;
    int m_B;
};

//Overloading shift left operator with global function
ostream & operator<<(ostream &out,Person &p)
{
    out<<"m_A="<<p.m_A<<" m_B="<<p.m_B;
    return out;
}

void test01()
{
    Person p(10,10);
    cout<<p<<" Hello world!"<<endl;
}

int main()
{
    test01();
    return 0;
}

4.3 increment operator overloading

#include <iostream>
using namespace std;

//Overload increment operator

//Custom integer
class MyInteger {
    friend ostream &operator<<(ostream &cout, MyInteger myint);
public:
    MyInteger()
    {
        m_Num = 0;
    }

    //Overloading the pre + + operator return reference is to always increment a data
    MyInteger &operator++()
    {
        //Perform + + operation first
        m_Num++;
        //Then make a return
        return *this;
    }

    //Overload post + + operator
    //This int represents a placeholder parameter, which can be used to distinguish between pre and post increment
    MyInteger operator++(int)//Post increment returns a value, not a reference
    {
        //Return results first
        MyInteger temp = *this;
        //Post increment
        m_Num++;
        //Finally, the recorded results are returned
        return temp;
    }

private:
    int m_Num;
};

//Overload shift left operator
ostream &operator<<(ostream &cout, MyInteger myint)
{
    cout<<myint.m_Num;
    return cout;
}

void test01()
{
    MyInteger myint;
    cout<<++myint<<endl;
}

void test02()
{
    MyInteger myint;
    cout<<myint++<<endl;
}

int main()
{
    test01();
    test02();
    return 0;
}

4.4 overload of assignment operator

#include <iostream>
using namespace std;
//Assignment operator overload
class Person
{
public:
    // Age to pile area
    Person(int age)
    {
        m_Age = new int(age);  // All objects from new are memory addresses
    }

    //  Write the attribute age as a pointer
    int *m_Age;

    // Destructor, the programmer controls the memory
    ~Person()
    {
        if (m_Age != NULL)
        {
            delete m_Age; // Delete the occupied memory and empty it
            m_Age = NULL;
        }
    }

    Person& operator=(Person &p)
    {
        //First, you should first judge whether there are attributes in the heap area. If so, you should release them first
        if (m_Age != NULL)
        {
            delete m_Age;
            m_Age = NULL;
        }
        //Then deep copy to solve the problem of shallow copy
        m_Age = new int(*p.m_Age);
        //Finally, return to itself
        return *this;
    }
};

void test01()
{
    Person p1(18);
    Person p2(20);
    Person p3(30);
    //Assignment operation
    p3 = p2 = p1;//For the problem of shallow copy, repeatedly free the memory in the heap

    cout << "p1 Your age is: " << *p1.m_Age << endl;
    cout << "p2 Your age is: " << *p2.m_Age << endl;
    cout << "p3 Your age is: " << *p3.m_Age << endl;
}

int main()
{
    test01();
    return 0;
}

4.5 overloading of relational operators

#include<iostream>
#include <stdio.h>
#include <string>
using namespace std;

//Relational operator overloading
class Person {
public:
    Person (string name,int age)
    {
        m_Name= name;
        m_Age = age;
    }
//    Overload = = number
    bool operator==(Person &p)
    {
        if(this->m_Name==p.m_Name && this->m_Age ==p.m_Age)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    string m_Name;
    int m_Age;

};
void test01(){
    Person p1("WangTwo ",18);
    Person p2("WangTwo ",19);
    if(p1==p2)
    {
        cout<<"equal";
    }
    else
    {
        cout <<"Unequal";
    }

}
int main(){
    test01();
    return 0;
}

4.6 function call operator overloading

The function call operator () can be overloaded. Because the method used after overloading is very similar to the function call, it is called imitation function.

#include <iostream>
#include <stdio.h>
using namespace std;

//Function call operator overloading
class MyPrint
{
public:
    void operator()(string test)
    {
        cout <<test <<endl;
    }
};

void test01(){
    MyPrint myprint ;
    myprint("Hello World!");//Because it is very similar to the call of a function, it is called an imitation function
}

class MyAdd
{
public:
    int operator()(int num1 ,int num2)
    {
        return num1+num2;
    }
};
void test02()
{
    MyAdd myadd;
    int sum = myadd(100,200);
    cout <<	"sum="<<sum<<endl;
//MyAdd() anonymous function object
    cout <<MyAdd()(100,100);
}
int main(){
    test01();
    test02();
    return 0;
}

5, Inherit

Inheritance is one of the three characteristics of object-oriented. The method of inheritance can effectively reduce duplicate code.

5.1 basic syntax of inheritance

Syntax: class subclass: inheritance method parent class
class jpage : public page

#include <iostream>
using namespace std;

class basepage
{
public:
    void show()
    {
        cout << "Basic interface" << endl;
    }

};
class javapage : public basepage
{
public:
    void javashow()
    {
        cout << "java Interface" << endl;
    }
};

class cpppage : public basepage
{
public :
    void cppshow()
    {
        cout << "cpp Interface" << endl;
    }
};
class pythonpage : public basepage
{
public:
    void pythonshow()
    {
        cout << "python Interface" << endl;
    }
};

void test01(){
    javapage jav;
    jav.show();
    jav.javashow();
    cout << "**************" << endl;

    cpppage cp;
    cp.show();
    cp.cppshow();
    cout << "**************" << endl;

    pythonpage py;
    py.show();
    py.pythonshow();
    cout << "**************" << endl;
}

int main()
{
    test01();
    return 0;
}

5.2 mode of inheritance

Syntax: class subclass: inheritance method parent class
Methods: public inheritance, protected inheritance and private inheritance

#include <iostream>
using namespace std;

//Parent class 1
class Base1
{
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};

//1. Public succession
class Son1 :public Base1
{
public:
    void func()
    {
        m_A; //The public permission member in the parent class is still the public permission inherited from the child class
        m_B; //Protected permission members in the parent class are still protected permissions inherited from the child class
        //m_C; // private permission member in the parent class. The child class is inaccessible
    }
};

void test01()
{
    Son1 s1;
    s1.m_A = 100; //The parent class is inherited into Son1 and can be accessed by public permissions
//    s1.m_B;// An error is reported in Son1 m_B is the protection permission, which cannot be accessed outside the class
}

//Parent class 2
class Base2
{
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};

//2 protection of inheritance
class Son2:protected Base2
{
public:
    void func()
    {
        m_A; //The public permission member in the parent class inherits the protected permission in the child class
        m_B; //Protected permission members in the parent class are still protected permissions inherited from the child class
        //m_C; // private permission member in the parent class. The child class is inaccessible
    }
};
void test02()
{
    Son2 s;
    //s.m_A; // protected permission in subclass, not accessible outside class
}

//3 private inheritance
class Base3
{
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};
class Son3:private Base3
{
public:
    void func()
    {
        m_A; //The public permission member in the parent class inherits the private permission in the child class
        m_B; //The protected permission member in the parent class is still a private permission inherited from the child class
        //m_C; // private permission member in the parent class. The child class is inaccessible
    }
};

void test03()
{
    Son3 s;
//    s.m_A = 1000; // The private permission member in the subclass is not accessible outside the class
}

class GrandSon3 :public Son3
{
public:
    void func()
    {
        //Son3 is a private inheritance, so the properties of inherited son3 cannot be accessed in GrandSon3
        //m_A;
        //m_B;
        //m_C;
    }
};

int main(){

    return 0;
}

5.3 object model in inheritance

#include <iostream>
using namespace std;

class Base
{
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C; 
};

//Public inheritance
class Son :public Base
{
public:
    int m_D;
};

void test01()
{
//    All non static members in the parent class will be inherited by the child class
//    The private member attribute in the parent class is hidden by the compiler, so it cannot be accessed
    cout << "sizeof Son = " << sizeof(Son) << endl;//16
}

int main() {
    test01();
    return 0;
}

5.4 construction and deconstruction sequence in inheritance

#include <iostream>
using namespace std;
//Construction and deconstruction order in inheritance
class Base
{
public:
    Base()
    {
        cout << "Base Constructor!" << endl;
    }
    ~Base()
    {
        cout << "Base Destructor!" << endl;
    }
};

class Son : public Base
{
public:
    Son()
    {
        cout << "Son Constructor!" << endl;
    }
    ~Son()
    {
        cout << "Son Destructor!" << endl;
    }
};

void test01()
{
//    Base b;
    //The order of construction and destruction in inheritance is as follows:
    // The parent class is constructed first, and then the child class is constructed. The destruction order is opposite to the construction
    Son s;
}

int main() {
    test01();
    return 0;
}


5.5 handling method of members with the same name in inheritance

#include <iostream>
using namespace std;

//Handling of members with the same name in inheritance
class Base {
public:
    Base()
    {
        m_A = 100;
    }

    void func()
    {
        cout << "Base - func()call" << endl;
    }

    void func(int a)
    {
        cout << "Base - func(int a)call" << endl;
    }

public:
    int m_A;
};


class Son : public Base {
public:
    Son()
    {
        m_A = 200;
    }
    
    void func()
    {
        cout << "Son - func()call" << endl;
    }
public:
    int m_A;
};

void test01()
{
    Son s;
    cout << "Son Lower m_A = " << s.m_A << endl;//200
    cout << "Base Lower m_A = " << s.Base::m_A << endl;//100

    s.func();//Subclass objects can directly access members with the same name in subclasses
//    If a member function with the same name hidden in the parent class appears in the subclass, the member with the same name in the subclass will hide all member functions with the same name in the parent class
    s.Base::func();//The subclass object plus the scope of the parent class can access members with the same name under the parent class
    s.Base::func(10);

}
int main() {
    test01();
    return 0;
}

5.6 processing method of static members with the same name in inheritance

#include <iostream>
using namespace std;

//Handling of static members with the same name in inheritance
class Base {
public:
    static void func()
    {
        cout << "Base - static void func()" << endl;
    }
    static void func(int a)
    {
        cout << "Base - static void func(int a)" << endl;
    }

    static int m_A;
};

int Base::m_A = 100;

class Son : public Base {
public:
    static void func()
    {
        cout << "Son - static void func()" << endl;
    }
    static int m_A;
};

int Son::m_A = 200;

//Static member property with the same name
void test01()
{
    //1 access by object
    cout << "Access via object: " << endl;
    Son s;
    cout << "Son  lower m_A = " << s.m_A << endl;
    cout << "Base lower m_A = " << s.Base::m_A << endl;

    //2 access by class name
    cout << "Access by class name: " << endl;
    cout << "Son  lower m_A = " << Son::m_A << endl;
    cout << "Base lower m_A = " << Son::Base::m_A << endl;
}

//Static member function with the same name
void test02()
{
    //Access by object
    cout << "Access via object: " << endl;
    Son s;
    s.func();
    s.Base::func();

    cout << "Access by class name: " << endl;
    Son::func();
    Son::Base::func();
    //In case of the same name, the subclass will hide all member functions with the same name in the parent class and need to add scope access
    Son::Base::func(100);
}
int main() {

    test01();
    test02();
    return 0;
}

5.7 multi inheritance syntax

In C + +, one class is allowed to inherit multiple classes
Syntax: class subclass: inheritance method parent class 1, inheritance method parent class 2
In multiple inheritance, a member with the same name may appear in the parent class, which needs to be distinguished by scope

#include <iostream>
using namespace std;

//Multiple inheritance syntax
//Create parent class 1
class Base1 {
public:
    Base1()
    {
        m_A = 100;
    }
public:
    int m_A;
};

//Create parent class 2
class Base2 {
public:
    Base2()
    {
        m_A = 200;  //Start with m_B will not be a problem, but changing to mA will cause ambiguity
    }
public:
    int m_A;
};

//Subclasses inherit parent 1 and parent 2
//Syntax: class subclass: inheritance method parent class 1, inheritance method parent class 2
class Son : public Base2, public Base1
{
public:
    Son()
    {
        m_C = 300;
        m_D = 400;
    }
public:
    int m_C;
    int m_D;
};

//Multi inheritance can easily lead to the situation of members with the same name
//By using the class name scope, you can distinguish which members of the base class are called
void test01()
{
    Son s;
    cout << "sizeof Son = " << sizeof(s) << endl;//16,
    cout << s.Base1::m_A << endl;//100
    cout << s.Base2::m_A << endl;//200
}

int main() {

    test01();
    return 0;
}

5.8 diamond inheritance

Two derived classes inherit the same base class;
There are two classes that inherit the two derived classes at the same time;

#include <iostream>
using namespace std;

//diamond inheritance 
//Animals
class Animal
{
public:
    int m_Age;
};
//Using virtual inheritance to solve the problems caused by diamond inheritance (Alpaca has two ages after diamond inheritance, which is unreasonable)
//After adding the virtual keyword before inheritance, it becomes virtual inheritance
//At this time, the public parent class Animal is called the virtual base class
class Sheep : virtual public Animal {};//Sheep
class Tuo   : virtual public Animal {};//Camels
class SheepTuo : public Sheep, public Tuo {};//Alpacas

void test01()
{
    SheepTuo st;
    st.Sheep::m_Age = 10;
    st.Tuo::m_Age = 20;

    cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
    cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;
    cout << "st.m_Age = " << st.m_Age << endl;
}

int main() {
    test01();
    return 0;
}

6, Polymorphism

6.1 basic concepts of polymorphism

Polymorphisms fall into two categories:
(1) Static polymorphism: function overloading and operator overloading belong to static polymorphism and reuse function names;
(2) Dynamic polymorphism: derived classes and virtual functions implement runtime polymorphism.
Difference between static polymorphism and dynamic polymorphism:
(1) Statically polymorphic function address early binding - the function address is determined at the compilation stage
(2) Dynamic polymorphic function address late binding - function address determined at run time

#include <iostream>
using namespace std;

//Basic concepts of polymorphism
//Animals
class Animal
{
public:
    //The Speak function is an imaginary function
    //The virtual keyword is added in front of the function to become a virtual function, so the compiler cannot determine the function call when compiling.
    virtual void speak()
    {
        cout << "Animals are talking" << endl;
    }
};
//Cats
class Cat :public Animal
{
public:
    void speak()
    {
        cout << "The kitten is talking" << endl;
    }
};

class Dog :public Animal
{
public:

    void speak()
    {
        cout << "The dog is talking" << endl;
    }

};
//If we want to pass in any object, we will call the function of any object
//If the function address can be determined at the compilation stage, then static binding
//If the function address can only be determined at the running stage, it is dynamic binding

//Function to execute speech
//The address is bound early, and the function address is determined at the compilation stage
//If you want to let the cat talk, the function address cannot be bound in advance. It needs to be bound in the run-time stage, and the address is bound late
void DoSpeak(Animal & animal)//Animal & animal = cat;
{
    animal.speak();
}
//
//The following conditions are met:
//1. There is an inheritance relationship
//2. Subclasses override virtual functions in the parent class
//Polymorphic use:
//A parent class pointer or reference points to a child class object

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

    Dog dog;
    DoSpeak(dog);
}

int main()
{
    test01();
    return 0;
}


6.2 pure virtual functions and abstract classes

In polymorphism, the implementation of virtual functions in the parent class is meaningless, which is mainly the content rewritten by calling subclasses. Therefore, virtual functions can be changed into pure virtual functions.

Pure virtual function syntax: virtual return value type function name (parameter list) = 0;
When there are pure virtual functions in a class, this class is also called an abstract class.

Abstract class features:
(1) Unable to instantiate object;
(2) Subclasses must override pure virtual functions in abstract classes, otherwise they also belong to abstract classes.

#include <iostream>
using namespace std;

//Pure virtual functions and abstract classes
class Base
{
public:
//    1, Pure virtual function: any pure virtual function in a class is called an abstract class
//    2, Abstract class characteristics
    //1 abstract class cannot instantiate object
    //2 a subclass must override the pure virtual function in the parent class, otherwise it also belongs to an abstract class

//    This is a pure virtual function: virtual return value type function name (parameter list) = 0;
    virtual void func() = 0;
};

class Son :public Base
{
public:
//Next, override the pure virtual function in the parent class
    virtual void func()
    {
        cout << "func function call" << endl;
    };
};

void test01()
{
//    Base b;//  Error, abstract class cannot instantiate object
//    new Base; Error, abstract class cannot instantiate object

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

int main()
{
    test01();
    return 0;
}

6.3 virtual deconstruction and pure virtual deconstruction

When polymorphism is used, if an attribute in a subclass is opened to the heap, 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.

Virtual destructor and pure virtual destructor have the following commonalities:
(1) It can solve the problem that the parent class pointer releases the child class object;
(2) All need to have specific function implementation.

Difference between virtual destruct and pure virtual destruct:
If it is a pure virtual destructor, this class belongs to an abstract class and cannot instantiate an object.

Virtual destructor syntax:
virtual ~ class name () {}

Pure virtual destructor syntax:
virtual ~ class name () = 0;
Class name:: ~ class name () {}

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

//Virtual destruct and pure virtual destruct
class Animal
{
public:
    Animal()
    {
        cout << "Animal Constructor call!" << endl;
    }
//    Pure virtual function
    virtual void Speak() = 0;

    //The virtual destructor is used to solve the problem that the subclass objects may not be cleaned up if they are released through the parent class pointer
    //The destructor plus the virtual keyword becomes a virtual destructor
    //virtual ~Animal()
    //{
    //	Cout < < "animal virtual destructor call!" < < endl;
    //}

//    Pure virtual deconstruction
    virtual ~Animal() = 0;
};

//Pure virtual destructors require declarations and concrete implementations
//With pure virtual destructor, this class is also an abstract class and cannot instantiate objects.
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);
    }
//    Overriding a pure virtual function in a parent class
    virtual void Speak()
    {
        cout << *m_Name <<  "The kitten is talking!" << endl;
    }

//    A destructor is provided to free heap memory
    ~Cat()
    {
        cout << "Cat Destructor call!" << endl;
        if (this->m_Name != NULL) {
            delete m_Name;
            m_Name = NULL;
        }
    }

public:
    string *m_Name;
};

void test01()
{
    Animal *animal = new Cat("Tom");
    animal->Speak();
    delete animal;
}

int main()
{
    test01();
    return 0;
}


Topics: C++