##0. Preface
Recently, when doing algorithm transplantation, I found that I had forgotten a lot about c + +, so I planned to pick it up again.
I feel shallow on paper. I never know that I have to practice it
It has always been the principle of my study and work. I prefer hands-on.
So I knocked the basic knowledge of c + + line by line, no matter how simple!
For future research camera framework source code, write algorithm transplantation framework to lay the foundation!
1. Memory partition model
c + + memory is divided into four areas:
-
Code area: it stores the binary code of the function body, which is managed by the operating system.
-
Global area: stores global variables, static variables and constants
-
Stack area: automatically allocated and released by the compiler to store function parameter values, local variables, etc.
-
Heap area: it is allocated and released by the programmer. If the programmer does not release it, it will be recycled by the operating system at the end of the program
Meaning of memory partition:
The data stored in different areas gives us different life cycles and gives us greater flexibility in programming.
1.1 before program operation
After the program is compiled, the exe executable program is produced. Before the program is executed, it is divided into
Code area:
deposit cpu Executed machine instructions 1.Sharing: the purpose is that for frequently executed programs, only one code is needed in memory. 2.Read only: prevent the program from being modified at will
Global area:
Global variables contain constant areas: string constants and other constants After the program ends, the system releases the data of the global area by itself const Modified local variable(Local constant),Not in the global area, but in the stack area int fun() { const int a =10;//Local constant }
Summary:
- c + + is divided into global area and code area before exe program runs
- The code area is characterized by read-only and sharing
- Global variables and static variable constants are stored in the global area
- The const modified global variables (i.e. global constants) and string constants are stored in the constant area
1.2 after program operation
Stack area
Automatically allocate and release function parameters, local variables, etc. by the compiler
Note: without returning the address of local variables, the compiler will automatically release the data opened by the stack after the function is finished
Heap area
By the programmer and release respectively. If the programmer does not release, the system will release itself after the program ends. If the program does not end, the system will never release.
c + + mainly uses new to apply for memory in the heap
1.3 new usage
When applying for new, be sure to use delete to release
int *a = new int(10); delete a; int *b = new int [8]; delete [] b;//Array release to add []
2. Reference
2.1 basic use of references
**Function: * * alias variables
**Syntax: * * data type & alias = original name
int a =10; int &b = a; (In essence: int *const b = &a)
2.2 notes for reference
- 1. The reference must be initialized
int &b;// error - 2. The reference cannot be changed after initialization
int a =10; int &b = a; int c =20; b = c;//Assignment operation instead of changing references
2.3 reference function parameters
**Function: * * when passing parameters to a function, you can use the reference technology to let the formal parameters modify the arguments
**Advantages: * * pointer modification arguments can be simplified
void swap(int &a,int &b) { int temp = a; a = b; b = temp; }
2.4 reference function return value
Function: a reference can be used as the return value of a function
Note: do not return references to local variables
Usage: function call as lvalue
int& fun() { static int a =10; return a; } int &ref = fun(); fun()= 100;//Function calls can be assigned as lvalues
2.5 nature of reference
Essence: the essence of reference is implemented internally in c + +. It is a pointer constant (the point is unchanged, and the value inside is variable)
//The compiler finds that this is a reference, and the conversion bit int * const ref = & A; void func(int & ref) { ref = 100;//Ref is a reference, conversion bit * ref = 100 } int main(){ int a=10; int& ref = a; //Automatically convert to int * const ref = & A; //Pointer constants refer to unchangeable and changeable values, which also explains why references are unchangeable ref = 20;//If it is found to be a reference, it will be automatically converted to * ref =20 }
Conclusion: the c + + approach uses reference counting because of its convenient syntax. The essence of reference is pointer constant, but the compiler has done all pointer operations for us
const reference
**Function: * * constant reference is mainly used to modify formal parameters to prevent misoperation
const int & a;
int &ref =10;// This is wrong. The reference must refer to a legal memory. 10 in the constant area, const must be added when accessing
int &ref =10;//This is an error. The reference must refer to a valid piece of memory const int &ref =10;//legitimate //After adding const, the compiler changes the code to /* int temp =10; const int &ref = temp; */ //Constant reference usage scenario: it is usually used to modify formal parameters to prevent function from changing values void printValue(const int&a){ //a=20; report errors print("a=%d",a); }
3. Function improvement
3.1 default parameters of function
In c + +, function parameters can have default values.
Syntax: return value function name (parameter = default) {}
int func(int a,int b=1, int c =2) { }
-
If there is a default parameter at a certain position in the formal parameter, there must be a default parameter from this position to the future, from left to right
-
If the function declaration has default parameters, the function implementation cannot have default parameters, otherwise an error will be reported: redefine the default parameters
int fun2(int a =10);//statement int func2(int a =10){//realization } This will report an error
3.2 function occupancy parameters
The formal parameters of functions in c + + can have placeholder parameters, which are used for placeholder. This position must be filled when calling functions.
Syntax: return value function name (data type) {}
//Placeholder parameter - second parameter void func(int a,int) { } //Placeholder parameters can also have default parameters void func2(int = 10){ } int main(){ func(10,10); }
3.3 function overloading
3.3.1 function overloading overview
**Function: * * function names can be the same to improve reusability
Function overload satisfies the condition
- Under the same scope
- Same function name
- Function parameters have different types, numbers or orders
**Note: * * the return value of a function cannot be used as a condition for function overloading
void fun(); void fun (int a,float b) void fun (float b,int a) //Int fun (float B, int a) this will report an error, which is ambiguous
3.3.2 precautions for function overloading
- Reference as overload condition
//1. Reference as overload condition void func(int &a){ //int& a =10 ; wrongful } void func(const int &a){const int &a = 10 legitimate } These two are function overloads int and const int Calculate different types int main(){ int a =10; func(a);//Call the first void func (int & A), //If void func (int &a) is not defined, the second one is called func(10);//Call the second void func (const int & A) }
- Function overload encountered default parameter
void func(int a){ //int& a =10 ; wrongful } void func(int a,int b =10){ } fun(10);//The compiler doesn't know which to call here
Here, the function will be ambiguous and error will be reported,
Generally, don't write default parameters when writing function overloads
4. Classes and objects
Everything is an object, including attributes and behaviors
Three characteristics of c + + object-oriented: encapsulation, inheritance and polymorphism
4.1 packaging
4.4.1 significance of packaging
Encapsulation is one of the three characteristics of c + + object-oriented
significance:
- Take attributes and behaviors as a whole to express things in life
- Control attributes and behaviors with permissions f
Meaning of encapsulation 1:
When designing classes, attributes and behaviors are written together to represent things
**Syntax: * * class class name {access rights: attribute / behavior};
Design a circle class
//PI const double PI = 3.14; class Circle { //Access rights public: //attribute int r;//radius //behavior double calZC(){ return 2*PI*r; } Circle c ;//object c.r = 10; c.calZC(); }
Meaning of encapsulation 2:
Control attributes and behaviors with permissions
There are three kinds of access rights
1.public: public permission
Members can be accessed inside the class and outside the class
2.protected: protected permission
Members can be accessed inside the class, but not outside the class
3.private: private permission
Members can be accessed inside the class, but not outside the class
4.1.2 differences between struct and class
In c + +, the only difference between struct and class is:
Different default access rights
- struct default permission public
- class default permission private
class C1 { int a;//private }; struct C2 { int a ;//public }
4.1.3 set member property to private
- Advantage 1: set all properties to private, and you can control the read and write permissions yourself
- **Advantage 2: * * for write permission, data validity can be detected
4.2 initialization and release of objects
4.2.1 constructors and destructors
Object initialization and release are two very important security issues.
The object does not use the initial state, and the consequences are unknown
If the object is not released after use, it will also cause difficult security problems
- Constructor: initializes properties when an object is created
- Destructor: releases resources when an object is destroyed
Both functions are called automatically by the system!
**Constructor syntax: * * class name () {}
1. The constructor does not return a value and does not write void
2. The function name and class name are the same
3. The constructor has parameters and can be overloaded
4. When creating an object, the constructor is automatically called once
Destructor syntax:
1. The destructor does not return a value or write void
2. The destructor is the same as the class name, and the destructor is added before the name~
3. The destructor has no parameters and cannot be overloaded
4. The destructor will be called automatically before the object is destroyed
Class p{ //Constructor p(){ } //Destructor ~p(){ } };
4.2.3 classification and calling of constructors
Two classification methods:
According to parameters: parametric construction and nonparametric construction
By type: normal construction and copy construction
Three call modes:
bracketing
Display method
Implicit transformation method
class Person { public: Person(){//Nonparametric structure } Person(int a){//Parametric structure age = a; } //The above are all ordinary structures Person(const Person &p){//copy construction age = p.age; } private: int age; } 1.bracketing Person p1; Person p2(10); Person p3(p2); 2.Display method Person p1; Person p2 = Person(10); Person p3 = Person(p2); Person(6);//It's an anonymous object Feature: after the execution of the current line, the system will immediately recycle anonymous objects matters needing attention: Do not use copy constructors to initialize anonymous objects Person(p3); The compiler will think Person(p3) == Person p3; 3.Implicit transformation method Person p4 = 10;//Equivalent to Person p4 = Person(10); Person p5 = p4;//Equivalent to Person p5 = Person(p4);
4.2.3 call timing of copy constructor
class Person{ public: Person(){ Nonparametric structure } Person(int a ){ Parametric structure age =a; } Person(const Person & p){ copy construction age = p.age; } ~Person(){ Destructor } private: int age; }
Three cases:
- Initialize a new object with an already created object
void test(){ Person p1(20); Person p2(p1); }
- Values are passed to function parameters
void doWork(Person p){//This is equivalent to Person p = my_p } void test2(){ Person my_p; doWork(my_p); } * Returns a local object as a value ```c Person doWork3(){ Person p; return p;//Here, p will be released after execution, and the system will copy a temporary p back } void test(){ Person p = doWrok3(); }
4.2.4 constructor calling rules
By default, the c + + editor adds three functions to a class
1. Default constructor (no parameter function body is empty)
2. Default destructor (nonparametric function body is empty)
3. Default copy constructor (copy values of all attributes)
Constructor rule:
- If the user defines a parameterized construct, the c + + editor will not provide a default nonparametric construct, but will provide a default copy construct
- If you define a copy constructor, c + + does not provide another constructor
Person(const Person & p){ copy construction age = p.age; }
4.2.5 deep and light copies
- Shallow copy: value copy
- Deep copy: reapply a piece of memory space in the heap for copy operation
#include <iostream> using namespace std; /* run this program using the console pauser or add your own getch, system("pause") or input loop */ class Person { public: Person() { cout<<"Person Default constructor "<<endl; } Person(int age,int height) { m_age = age; m_height = new int(height); cout<<"Person Parameterized constructor"<<endl; } //Shallow copy: the problem is that the heap is repeatedly released Person(const Person &p) { cout<<"Person Shallow copy constructor call"<<endl; m_age = p.m_age; m_height = p.m_height; } //Deep copy Person(const Person &p) { m_age = p.m_age; m_height = new int(*p.m_height); } ~Person() { cout<<"Person Destructor call,m_height address"<<m_height<<endl; if(m_height != nullptr) { cout<<"release person=%p"<<this<<" m_height ="<<*m_height<<endl; delete m_height; m_height = nullptr; } } int m_age; int *m_height; }; void test() { Person p1(18,180); cout<<"p1 ="<< (int*)&p1 << " "<<p1.m_age<<" "<<*p1.m_height<<endl; Person p2(p1); cout<<"p2 ="<<(int*)&p2<< " "<<p2.m_age<<" "<<*p2.m_height<<endl; } int main(int argc, char** argv) { test(); return 0; }
Summary: if the member variable has the ability to open up memory in the heap area, it is necessary to provide its own copy constructor to prevent the repeated release of heap memory in the shallow copy agent
4.2.6 initialization list
effect
c + + provides the syntax of initialization list to initialize attributes
**Syntax: * * constructor (): attribute 1 (value 1), attribute 2 (value 2) ·· {}
#include <iostream> using namespace std; /* run this program using the console pauser or add your own getch, system("pause") or input loop */ class Person { public: Person():m_a(10),m_b(20) { } Person(int a,int b):m_a(a),m_b(b) { } int m_a; int m_b; }; int main(int argc, char** argv) { Person p; cout<<p.m_a<<" "<<p.m_b<<endl; Person p2(30,40); cout<<p2.m_a<<" "<<p2.m_b<<endl; return 0; }
4.2.7 class objects as members of classes
When constructing: when other class objects are members of this class, the objects of other classes are constructed first, and the object itself is constructed
Destruct: first destruct yourself, and then destruct other classes. (because the loading function is in the form of stack, last in, first out)
class Phone { public: Phone(string name):name(name) { cout<<"Phone Parameterized constructor call"<<endl; //this.name = name; } ~Phone() { cout<<"Phone Destructor call"<<endl; } string name; } ; class Person { public: Person(string name,string p):name(name),p(p) { cout<<"Person Parameterized constructor call"<<endl; } ~Person() { cout<<"Person Destructor call"<<endl; } string name; Phone p; };
4.2.8 static members
Add the static keyword in front of member functions and member variables to become static members.
Static members are divided into:
- Static member variable
- All objects share one piece of data
- Allocate memory at compile time
- Inner class declaration, outer class initialization
class A { public: static int m_a;//Intra class declaration } int A::m_a = 100;//static should be removed for out of class initialization
- Static member function
- All objects share the same function
- Static member functions can only access static member variables
- Private member functions or variables cannot be accessed outside the class
class Person { public: static void fun() { cout<<"Static member function"<<endl; // m_a = 1; m_b = 2; } int m_a; static int m_b; priviate: static void fun2()// { } } ; void test() { //1. Call through object Person p; p.fun(); //2. Call by class name Person::fun(); //Private cannot be accessed //Person::fun2(); }
4.3 C + + object model and this pointer
4.3.1 member variables and member functions are stored separately
In c + +, member variables and member functions in a class are stored separately,
Only non static member variables belong to objects of a class
- Empty objects occupy 1 byte of memory
The c + + compiler allocates a byte space to each empty object to distinguish the memory occupied by the empty object.
class Person { } ; void test() { Person p;//Empty object cout<<sizeof(p)<<endl; }
- Only non static member variables belong to objects of a class
class Person { int a;//The non static member variable belongs to the object of the class. At this time, p's memory is 4 bytes static int b;//Static member variables do not belong to class objects void fun();//Non static member functions do not belong to class objects static void fun2();//Static member functions do not belong to class objects } ; void test() { Person p; cout<<sizeof(p)<<endl; }
4.3.2 concept of this pointer
The this pointer points to the object to which the called member function belongs
Whoever calls this, this points to which object
this is a pointer implicit in a non static member function
Purpose of this pointer:
- When a formal parameter has the same name as a member variable, it can be distinguished by the this pointer
- Return the object itself in the non static member function of the class. You can use return *this
class Person { public: Person(int age) { this->age = age; } //Here, you need to return Person &. If you return Person, the system will create a temporary variable to return Person& addAge(Person &p) { this->age += p.age; //This points to the pointer to p2, while * this points to the p2 object itself return *this; } int age; } ;
This points to the pointer to p2, while * this points to the p2 object itself
4.3.3 null pointer accessing member functions
c + + hollow pointers can also call member functions,
If you want to use this pointer, you should judge it to ensure the robustness of the code
class Person { public: void showName() { cout<<"this is Person class"<<endl; } void showAge() { if(this == NULL)//Increase robustness return; cout<<"age = "<<this->age<<endl;//p == null. Direct access here will crash } int age; }; int main(int argc, char** argv) { Person *p =NULL; p->showName(); p->showAge(); return 0; }
4.3.4 const modifier member function
Constant function
- A member function followed by the const keyword is called a constant function
- Member properties cannot be modified within a constant function
- After the keyword mutable is added to the member function, it can be modified in the constant function
Constant object:
- Adding const before declaring an object is called a constant object
- Constant objects can only call constant functions (because ordinary functions can change member variables, and constant objects are not allowed to modify member variables)
class Person { public: //Constant function //Add const after the member function to modify the point of this, and the value pointed by the pointer cannot be modified. Here this = const person * const this void showPerson() const { //this = (Person * const this) is essentially a pointer constant. The pointing of the pointer cannot be modified, and the pointing content is variable //this->m_ a = 100; No change allowed this->m_b =100; } void fun() { //You can modify the value of member variables, such as m_a =10; } int m_a; mutable int m_b;//mutable variable can modify the value even in constant functions }; //Constant object void test() { const Person p ;//Add const in front of the object to become a constant object //p.m_a = 100; Constant objects are not allowed to change any value p.m_b = 100; //Constant objects can only call constant functions p.showPerson(); //p.fun();// Constant objects cannot call ordinary member functions, because ordinary member functions can modify member variables, and constant objects are not allowed to modify }
4.4 friends
effect:
Let a function or class access private members in another class
friend keyword
Three implementations of friends:
- Global function as friend
class MyHome { //Global function as friend friend void goodGay(MyHome &m); public: MyHome():m_livingRoom("a living room"),m_bedRoom("bedroom") { } string m_livingRoom;//a living room private: string m_bedRoom;//bedroom } ; //Global function void goodGay(MyHome &m)//Here, it is equivalent to myhome * const M = & MH; { cout<<"Good friends are visiting:"<<m.m_livingRoom<<endl; cout<<"Good friends are visiting:"<<m.m_bedRoom; } int main(int argc, char** argv) { MyHome mh; goodGay(mh); return 0; }
- Class as friend
class Home { public: friend class GoodGay; Home():m_livingRoom("a living room"), m_bedRoom("bedroom") { } public: string m_livingRoom; private: string m_bedRoom; }; class GoodGay { public: GoodGay() { h = new Home; } void visit() { cout<<"Good friends are visiting:"<< h->m_livingRoom<<endl; cout<<"Good friends are visiting:"<< h->m_bedRoom<<endl; } private: Home *h; };
- Member function as friend
class GoodGay; class Home { //Member function as friend friend void GoodGay::visit(); public: Home():m_livingRoom("a living room"), m_bedRoom("bedroom") { } public: string m_livingRoom; private: string m_bedRoom; }; class GoodGay { public: GoodGay() { h = new Home; } void visit() { cout<<"Good friends are visiting:"<< h->m_livingRoom<<endl; cout<<"Good friends are visiting:"<< h->m_bedRoom<<endl; } private: Home *h; }; void test() { GoodGay g; g.visit(); }
4.5 heavy load
ps: operator overload skip
4.6 succession
Benefits of inheritance
Reduce duplicate code
4.6.1 basic syntax of inheritance
Syntax:
Class subclass: inheritance method parent class
Subclasses are also called derived classes
The parent class also becomes the base class
class BaspePage { public: void header() { cout<<"Home open class login registration"<<endl; } void footer() { cout<<"Help center, join us"<<endl; } } ; class Jave : public BaspePage { public: void content() { cout<<"This is JAVA course"<<endl; } }; int main(int argc, char** argv) { Jave j; j.header(); j.content(); j.footer(); return 0; }
4.6.2 mode of inheritance
There are three ways of inheritance:
- Public inheritance
- Protect inheritance
- Private inheritance
class Father { public: Father():a(1),b(1),c(1) { } public: int a; protected: int b; private: int c; }; class Son1:public Father { public: void fun() { a= 10; b= 20; //c= 30; } }; void test01() { Son1 s; s.a = 100; //s.b = 50; Cannot access outside the protection permission class }
4.6.3 object model in inheritance
Question: which members inherited from the parent class belong to the subclass object?
Private members will also be inherited by subclasses, but they are hidden by the compiler and cannot be accessed
class Base { public: int a; protected: int b; private: int c;//Private members will also be inherited by subclasses, but they are hidden by the compiler and cannot be accessed }; class Son:private Base { public: int d; }; int main(int argc, char** argv) { Son s; cout<<sizeof(s)<<endl;//size = 16 return 0; }
4.6.4 order of construction and Deconstruction in inheritance
After a subclass inherits the parent class, the construction of the parent class will also be called when creating a subclass object.
First construct the parent class, and then construct the child class. The deconstruction is the opposite: first construct the child class, and then the parent class
class Base { public: Base() { std::cout<<"Base Default constructor for"<<endl; } ~Base() { std::cout<<"Base Destructor for"<<endl; } }; class Son: public Base { public: Son() { std::cout<<"Son Default constructor for"<<endl; } ~Son() { std::cout<<"Son Destructor for"<<endl; } }; void test() { Son s; }
4.6.5 handling method of members with the same name in inheritance
Question: when a member with the same name appears between a subclass and a parent class, how can you access the data with the same name of the subclass or parent class through the subclass object?
- You can access the member with the same name in the subclass directly
- To access a member with the same name as the parent class, you need to add a scope
Summary:
- 1. Subclass objects can directly access members with the same name in subclasses
- 2. The subclass object plus scope can access the member with the same name as the parent class
- 3. When the subclass and the parent class have member functions with the same name, the subclass will hide all functions with the same name in the parent class. You need to add scope to access the functions with the same name in the parent class
class Base { public: Base() { m_a = 100; } void fun() { cout<<"Base - fun()"<<endl; } void fun(int a) { cout<<"Base - fun(int a)"<<endl; } int m_a; }; class Son : public Base { public: Son() { m_a = 200; } void fun() { cout<<"Son - fun()"<<endl; } int m_a; }; void test() { Son s; cout<<"Son Lower m_a = "<<s.m_a<<endl; //To access a member with the same name in the parent class through a subclass object, you need to add the scope Base cout<<"Base Lower m_a = "<<s.Base::m_a<<endl; } void test2() { Son s; s.fun(10); //To access a member with the same name in the parent class through a subclass object, you need to add the scope Base s.Base::fun(); //When a member function with the same name as the parent class appears in a subclass, the subclass will hide all member functions with the same name in the parent class //A hidden member function with the same name can only be accessed through the scope s.Base::fun(10); }
4.6.6 processing method of inheriting static members with the same name
Question: how do static members with the same name in inheritance access on subclass objects?
Static members and non static members have the same name and are handled in the same way
- You can access the member with the same name in the subclass directly
- To access a member with the same name as the parent class, you need to add a scope
//Access by object Son s; cout<<s.m_a<<endl; cout<<s.Base::m_a<<endl; //Access by class name cout<<Son::m_a<<endl; //The first:: represents access through the class name, and the second:: represents access under the scope of the parent class cout<<Son::Base::m_a<<endl;
Summary: the processing method of static members with the same name is exactly the same as that of non static members, except that there are two access methods (through object and class name)
4.6.7 multi inheritance syntax
Running a class in c + + inherits multiple classes
Syntax: class subclass: inheritance method parent class 1, inheritance method parent class 2···
Multiple inheritance may cause members with the same name to appear in the parent class, which needs to be distinguished by scope
Multi inheritance is not recommended in c + + development
class A { public: A():m_a(100) { } int m_a; }; class B { public: B():m_a(200) { } int m_a; }; class C : public A, public B { public: C() { m_c = 300; } int m_c; }; int main(int argc, char** argv) { C c; //When a member with the same name appears in the parent class, scope differentiation is required cout<<c.A::m_a<<" "<<c.B::m_a<<" "<<c.m_c<<endl; return 0; }
4.6.8 diamond inheritance
Concept:
2 subclasses inherit the same parent class
Another class inherits 2 subclasses
This inheritance is called diamond inheritance, or diamond inheritance
class Animal { public: int m_Age; }; //Virtual keyword is added before inheritance, which becomes virtual inheritance //At this time, the public parent class Animal is called the virtual base class class Sheep : virtual public Animal{};//sheep class Camel : virtual public Animal{};//camel class Alpaca : public Sheep , public Camel{};//Alpaca int main() { Alpaca a; a.Sheep::m_Age = 100; a.Camel::m_Age = 200; cout<<"a.Sheep::m_Age ="<<a.Sheep::m_Age<<endl; cout<<"a.Camel::m_Age ="<<a.Camel::m_Age<<endl; cout<<"am_Age ="<<a.m_Age<<endl;//All three prints are 200, and there is only one copy of data return 0; }
Summary:
- The main problem caused by diamond inheritance is that subclasses inherit multiple copies of the same data, resulting in a waste of resources.
- Using virtual inheritance to solve diamond inheritance problem
- The essence of virtual inheritance is to inherit pointers and access the same data (m_Age) through pointers
4.7 polymorphism
Polymorphism is one of the three characteristics of C + + object-oriented
4.7.1 basic concept of polymorphism
Polymorphisms fall into two categories:
- Static polymorphism: function overloading and operator overloading belong to static polymorphism and reuse function names
- Dynamic polymorphism: derived classes and virtual functions implement runtime polymorphism
Difference between static polymorphism and dynamic polymorphism:
- Statically polymorphic function address early binding - the function address is determined at the compilation stage
- Dynamic polymorphic function address late binding - function address determined at run time
Static polymorphism
class Animal { public: void speak() { cout<<"Animals are talking"<<endl; } } ; class Cat : public Animal { public: //Override: the return value of the function is exactly the same as the parameter list void speak() { cout<<"The cat is talking"<<endl; } }; //Address early binding determines the function address at the compilation stage //No matter what is passed, only the functions of the parent class will be called void doSpeak(Animal &a) { a.speak(); } void test() { Cat c; doSpeak(c); } int main(int argc, char** argv) { test();//Output: the animal is talking return 0; }
Dynamic polymorphism
class Animal { public: //Add virtual to become a virtual function, and you can bind late. Subclass functions can add virtual or not virtual void speak() { cout<<"Animals are talking"<<endl; } } ;
Override: the return value of the function is exactly the same as the parameter list
Overload: the function parameter list is different and the return value is the same
Summary:
Satisfaction conditions of dynamic polymorphism
- 1. Inheritance
- 2. The subclass overrides the virtual function of the parent class
Use of dynamic polymorphism
The pointer or reference of the parent class to execute the child class object
class A { public: int fun(){} } here sizeof(A)= 1 class A { public: virtual int fun(){} } here sizeof(A)= 4,Because plus virtual ,fun It becomes a pointer(Pointers are 4 bytes)vfptr:Virtual function pointer
4.7.2 polymorphic case - calculator
Common code implementation calculator
//Common code implementation calculator class NomalCal { public: int getResutl(int oper) { switch(oper) { case '+' : return m_a + m_b; break; case '-' : return m_a - m_b; break; case '*' : return m_a * m_b; break; //If you want to extend the function: Division, you need to modify the source code //Open close principle is advocated in development: open the extension and close the modification default: return 0; } } int m_a; int m_b; } ; void test1() { NomalCal nc; nc.m_a = 10; nc.m_b = 10; cout<<nc.m_a<< " + " <<nc.m_b<<" = "<<nc.getResutl('+')<<endl; }
Polymorphic implementation calculator
//Polymorphic implementation class BaseCal { public: virtual int getResult(){return 0;} int m_a; int m_b; } ; class AddCal : public BaseCal { int getResult() { return m_a+m_b; } }; Other functions continue to be added, such as subtraction void test2() { //Polymorphic usage conditions: parent class pointer or reference points to objects such as BaseCal *bc = new AddCal; bc->m_a = 20; bc->m_b = 10; cout<<bc->m_a<< " + " <<bc->m_b<<" = "<<bc->getResult()<<endl; delete bc;//Remember to release }
Advantages of polymorphism:
- Clear code structure
- Strong readability
- It is conducive to the expansion and maintenance in the early and later stages, and conforms to the opening and closing principle
4.7.3 pure virtual functions and abstract classes
In polymorphism, usually the virtual function in the parent class is meaningless and mainly calls the overridden function of the child class.
Therefore, we can change the virtual function to pure virtual function
Syntax: virtual return value type function name (parameter list) = 0
When there is only one pure virtual function in a class, the class is also called an abstract class
Characteristics of abstract classes:
- Unable to instantiate object
- Subclasses must override pure virtual functions in abstract classes, otherwise they also belong to abstract classes
class Base//Abstract class: as long as there is a pure virtual function in the class { public: //Pure virtual function virtual void fun() = 0; } ; class Son : public Base { public: virtual void fun(){} };
4.7.4 case 2 - making drinks
1. Abstract class of tea
class AbsDrinKing { public: //1. Boiling water virtual void boil() = 0; //2. Brewing virtual void brew() = 0; //3. Pour into the cup virtual void pourInCup() = 0; //4. Add auxiliary materials virtual void putSomething() = 0; void makeDrink() { boil(); brew(); pourInCup(); putSomething(); } };
2. Make coffee
class CofferDrink : public AbsDrinKing { //1. Boiling water virtual void boil() { cout<<"Cook Coffee"<<endl; }; //2. Brewing virtual void brew() { cout<<"make coffee"<<endl; } //3. Pour into the cup virtual void pourInCup() { cout<<"Pour the coffee into the cup"<<endl; } //4. Add auxiliary materials virtual void putSomething() { cout<<"Add sugar"<<endl; } };
3. Making milk tea is the same
Omit···
4. Make function
void doWork(AbsDringKing *abs) { abs->makeDrink(); }
4.7.5 virtual destruct and pure virtual destruct
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 of the parent class to virtual destructor or pure virtual destructor
Virtual destructor and pure virtual destructor have the following commonalities:
- It can solve the problem of releasing subclass objects from parent class pointers
- All need to have specific function implementation
Difference between virtual destruct and pure virtual destruct:
- If it is a pure virtual destructor, the class belongs to an abstract class and cannot instantiate an object, but a virtual destructor can!
Virtual destructor syntax:
virtual class name () {}
Pure virtual destructor syntax:
virtual class name () = 0;
Write an implementation outside the class
Class name:: ~ class name () {}
Possible memory leaks
terms of settlement:
1. The parent destructor plus virtual becomes a virtual destructor
class Animal { public: Animal(){cout<<"Animal constructor call "<<endl;} virtual ~Animal(){cout<<"Animal Virtual destructor call"<<endl;}//Virtual deconstruction virtual void speak() = 0; };
2. Pure virtual deconstruction
class Animal { public: Animal(){cout<<"Animal constructor call "<<endl;} virtual ~Animal() = 0;//Pure virtual deconstruction virtual void speak() = 0; }; Animal::~ Animal() { //If the parent class also applies for heap memory, it can be released here cout<<"Animal Pure virtual destructor call"<<endl; }
Summary:
- 1. Virtual destruct and pure virtual destruct are used to solve the situation of releasing subclass objects through parent class pointers
- 2. If there is no heap data in the subclass, you can not write virtual destruct or pure virtual destruct
- 3. Classes with pure virtual functions also belong to abstract classes
4.7.6 case 3 - computer assembly
//Abstract parts //1. Abstract CPU class CPU { public: virtual void cal() = 0; }; //2. Abstract graphics card class VideoCard { public: virtual void display() = 0; }; //3. Abstract memory class Memory { public: virtual void storage() = 0; } ; //Computer class Computer { public: Computer(CPU *cpu,VideoCard *vc,Memory * mem) { m_cpu = cpu; m_vc = vc; m_mem = mem; cout<<"Computer Constructor"<<endl; } ~ 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; } cout<<"Computer Destructor releases resources"<<endl; } void doWork() { //The parent class pointer calls the child class object m_cpu->cal(); m_vc->display(); m_mem->storage(); } private: CPU *m_cpu; VideoCard *m_vc; Memory *m_mem; }; //intel computer class IntelCPU : public CPU { public: void cal(){cout<<"Intel cpu Work"<<endl;} }; class IntelVideoCard : public VideoCard { public: void display(){cout<<"Intel The graphics card is working"<<endl;;} }; class IntelMemory : public Memory { public: void storage(){cout<<"Intel Memory is working"<<endl;} }; //Apple omits*** void test() { //First computer IntelCPU * ic = new IntelCPU; IntelVideoCard *ivc = new IntelVideoCard; IntelMemory *imem = new IntelMemory; Computer *com1 = new Computer(ic,ivc,imem); com1->doWork(); delete com1; } int main(int argc, char** argv) { test(); return 0; }
5. File operation
There are two types of documents:
**1. Text file: * * the text is stored in the computer in ASCII code
**2. Binary file: * * text is stored in binary form in the computer
Header file:
Three categories of operation documents:
1.ofstream: write operation
2.ifstream: read operation
3.fstream: read / write operation
5.1 text documents
5.1.1 writing documents
Write file steps:
1. Include header file
#include
2. Create a flow object
ofstream ofs;
3. Open the file
ofs.open("file path", opening method)
4. Write data
Ofs < < "data";
5. Close the file
ofs.close();
How files are opened:
Open mode | explain |
---|---|
ios::in | read |
ios::out | write |
ios::ate | Initial location: end of file |
ios::app | Write file in append mode |
ios::trunc | If the file exists, delete it before creating it |
ios::binary | Binary form |
//Text file write file void test() { ofstream ofs; ofs.open("test.txt",ios::out); ofs<<"Zhang San 18"; ofs.close(); }
5.1.2 document reading
To read a file:
1. Include header file
#include
2. Create a flow object
ifstream ifs;
3. Open the file
ifs.open("file path", opening method)
4. Write data
Four ways to read;
5. Close the file
ifs.close();
void test() { ifstream ifs; ifs.open("test.txt",ios::in); if(!ifs.is_open()) { cout<<"File open failed"<<endl; return; } //Method 1: read data // char buf[1024] = {0}; // while(ifs >> buf) // { // cout<<buf<<endl; // } //Method 2: read data // char buf[1024] = {0}; // while(ifs.getline(buf,sizeof(buf))) // { // cout<<buf<<endl; // } //Method 3: read data // string buf; // while(getline(ifs,buf)) // { // cout<<buf<<endl; // } //Mode 4: read data char c; while((c = ifs.get())!=EOF) { cout<<c; } ifs.close(); }
5.2 binary files
Read and write files in binary mode,
The opening method should be specified as ios::binary
5.2.1 writing files in binary mode
class Person { public: char m_Name[64]; int m_age; } ; void test() { //1. Import header file #include < fstream > //2. Create a flow object ofstream ofs;//Or ofs("person.txt",ios::out|ios::binary); //3. Open the file ofs.open("person.txt",ios::out|ios::binary); //4. Writing documents Person p = {"Zhang San",18}; ofs.write((const char*)&p,sizeof(Person)); //5. Close the file ofs.close(); }
5.2.2 reading files in binary mode
Reading files in binary mode mainly uses the member function read of stream object
Function prototype: istream & read (char * buffer, int len);
class Person { public: char m_Name[64]; int m_age; } ; void test() { //1. Import header file //2. Create a flow object ifstream ifs; //3. Open the file ifs.open("person.txt",ios::in); if(!ifs.is_open()) { cout<<"File opening failed!"<<endl; return; } // 4. Reading documents Person p; ifs.read((char *)&p,sizeof(Person)); cout<<p.m_age<<" "<<p.m_Name<<endl; }
Continue to be a salted fish ( ̄)  ̄)!