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; }