C + + core programming


Object oriented programming idea

1, Memory partition model

When the C + + program is executed, the 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: store global variables, static variables and constants
  • Stack area: automatically allocated and released by the compiler to store the parameter values and local variables of the function
  • 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

The meaning of four memory areas: the data stored in different areas gives us different life cycles and gives us greater flexibility in programming

1. Before program execution

After the program is compiled, it is generated exe class executes the program. Before executing the program, it is divided into two areas

Code area

  • Store mechanical commands executed by CPU
  • The code area is shared. The purpose of sharing is to have only one code in memory for programs that are frequently executed
  • The code area is read-only because it prevents the program from accidentally modifying its instructions

Global area

  • Global and static variables are stored here
  • The global area also includes a constant area, where string constants and other constants are also stored
  • The number of this area is released by the operating system at the end of the program
#include <iostream>
using namespace std;

// global variable
int global_a = 10;
int global_b = 10;

// Global constant
const int c_g_a = 10;
const int c_g_b = 10;

int main() {
	// Global area
	// Global variable, static variable, constant

	// Static variable
	static int static_a = 10;
	static int static_b = 10;

	// constant
	// string constant 
	cout << "The address of the string constant is " << (int)&"hello" << endl;
	// const modified variable
	// const modified global variable
	// const modified local variable
	const int c_l_a = 10;
	const int c_l_b = 10;


	// Create a normal local variable
	int a = 10;
	int b = 10;
	cout << "local variable a Your address is " << (int)&a << endl;
	cout << "local variable b Your address is " << (int)&b << endl;
	cout << "Local constant c_l_a Your address is " << (int)&c_l_a << endl;
	cout << "Local constant c_l_b Your address is " << (int)&c_l_b << endl;
	cout << "global variable global_a Your address is " << (int)&global_a << endl;
	cout << "global variable global_b Your address is " << (int)&global_b << endl;
	cout << "Static variable static_a Your address is " << (int)&static_a << endl;
	cout << "Static variable static_b Your address is " << (int)&static_b << endl;
	cout << "Global constant c_g_a Your address is " << (int)&c_g_a << endl;
	cout << "Global constant c_g_b Your address is " << (int)&c_g_b << endl;
}

2. After the program runs

Stack area

  • It is automatically allocated and released by the compiler to store the parameter values and global variables of the function
  • Note: do not return the address of local variables. The data opened up in the stack area is automatically released by the compiler
#include <iostream>
using namespace std;

int* func(int b) {  // The formal parameter data will also be placed in the stack area
   	 b = 100;
	int a = 10;  // Local variables are in the stack area and are cleared after the function is run
	return &a;  // Return the address of local variable, illegal function
}

int main() {
	cout << *func() << endl;  // The first time you can print the correct number is because the compiler makes a reservation
	cout << *func() << endl;  // The second data may not be retained
}

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
  • In C + +, new is mainly used to open up memory in heap area
#include <iostream>
using namespace std;


int* func() {
	// The new keyword can be used to open up data to the heap area
	int* p = new int(10);  // The pointer is also a local variable in nature. It is placed on the stack, and the data saved by the pointer is placed on the heap
	return p;
}

int main() {
	// Open up data in the heap area
	int* p = func();
	cout << *p << endl;
}

3. new operator

Using new operator in C + + to open up data in heap

The amount of data opened up in the heap area is manually opened up and released by the programmer. The release uses the operator delete

Syntax: new data type

The data created with new will return the pointer of the type corresponding to the data

#include <iostream>
using namespace std;


int* func() {
	// The new method opens up an array
	int* p = new int(10);  // Returns a pointer to the data type
	return p;
}

void test() {
 //Create an array of 10 integer data in the heap area 
	int* arr = new int[10];  // 10 means that the array has 10 elements
	for (int i = 0; i < 10; i++) {
		arr[i] = i;  // Assign a value to an array
	}
	// Release the heap array. When releasing the array, add []
	delete[] arr;
}

int main() {
	int* p = func();
	cout << *p << endl;  // The data in the heap area is managed by the programmer
	delete p;  // The data has been released, and it is illegal to access it again
	 cout << *p << endl;
	test();
}

2, Quote

1. Basic use

Function: alias variables

Syntax: data type & alias = variable name;

#include <iostream>
using namespace std;


int main() {
	// Reference basic syntax
	// Data type & alias = variable name
	int a = 10;
	int& b = a;
	cout << b << endl;
	b = 100;
	cout << a << endl;
}

2. Precautions

  • References must be initialized
  • The reference cannot be changed after initialization
#include <iostream>
using namespace std;


int main() {
	int a = 10;
	// References must be initialized
	int& b = a;
	// int &b;  //  Illegal operation
	// Once a reference is initialized, its address cannot be changed, but it can be assigned
}

3. Reference as function parameter

Function: when passing a function parameter, you can use the reference technology to make the formal parameter modify the argument

Advantages: the pointer can be modified

#include <iostream>
using namespace std;


// Exchange function
// 1. Value transfer
void swap(int a,int b) {
	int temp = a;
	a = b;
	b = temp;
}

// 2. Address delivery
void swap1(int* a, int* b) {
	int temp = *a;
	*a = *b;
	*b = temp;
}

// 3. Reference passing
void swap3(int &a, int &b) {  // &A is the alias, which can be the same as the variable name
	int temp = a;
	a = b;
	b = temp;
}


int main() {
	int a = 10;
	int b = 20;

	cout << a << b << endl;
	cout << "Run function" << endl;
	swap(a, b);
	cout << a << b << endl;
	// The arguments have not changed

	cout << a << b << endl;
	cout << "Run function" << endl;
	swap1(&a, &b);
	cout << a << b << endl;
	// Argument changed


	cout << a << b << endl;
	cout << "Run function" << endl;
	swap3(a, b);
	cout << a << b << endl;
	// Argument changed
}

The effect of referencing parameters is the same as passing by address, and the syntax of reference is clearer and simpler

4. Reference the return value of the function

Function: a reference can exist as the return value of a function

Note: do not return references to local variables

Usage: function call as lvalue

#include <iostream>
using namespace std;

// Reference the return value of the function
// 1. Do not return a reference to a local variable
int& test1() {  // Adding & is equivalent to returning by reference
	int a = 10;  // Local variables are stored in the stack area in four areas
	return a;
}

// 2. Function can be called as an lvalue
int& test2() {
	static int a = 10;  // Static variables are stored in the global area, and the data on the global area is released by the system after the program is completed
	return a;			
}

int main() {
	int& b = test1();
	cout << b << endl;
	cout << b << endl;
	int& c = test2();
	cout << c << endl;
	test2() = 1000;  // The left value of the memory can be modified as the address of the function
	cout << c << endl;
}

5. Essence

Essence: the essence of reference is a pointer constant implemented in C + +

6. Constant reference

Function: constant reference is mainly used to modify formal parameters to prevent misoperation

Use in function parameter list

#include <iostream>
using namespace std;


// print data
void showValue(int &a) {  // const can be added to prevent misoperation
	a = 1100;
	cout << a << endl;
}

int main() {
	// Constant reference: used to modify formal parameters to prevent misoperation
	int a = 10;
	const int& ref = 10;  // After const is added, the compiler modifies the code to int temp = 10; const int & ref = temp;
	// ref = 20;  //  After adding const, it becomes read-only
	showValue(a);
	cout << a << endl;
}

3, Function improvement

1. Default parameters

In C + +, the formal parameters in the formal parameter list of a function can have default values

Syntax: return value type function name (parameter = default) {}

#include <iostream>
using namespace std;


// Default parameters for function
void func(int a , int b = 10) {  // You can also pass in parameters. If you pass in parameters, you can use custom parameters. Otherwise, you can use default parameters
	cout << a << "\t" << b << endl;
}

// matters needing attention
// If a location already has a default parameter, it must have a default value from this location
// If the function declaration has default parameters, the function implementation cannot have default parameters and cannot redefine parameters, that is, only one declaration and implementation can have default parameters
void func(int a, int b);

int main() {
	int a = 20;
	// int b = 30;
	func(a);
}

2. Occupancy parameter

There can be placeholder parameters in the formal parameter list of functions in C + +, which are used for placeholder. This position must be filled when calling functions

Syntax: return value type function name (data type) {}

#include <iostream>
using namespace std;


// Placeholder parameter: there can be default parameters
// Return value type function name (data type) {}
void func(int a, int) {
	cout << "this is func" << endl;
}

void func(int a, int);

int main() {
	func(10, 20);
}

3. Function overloading

3.1 general

Function: function names can be the same to improve the reusability of function names

Conditions for function overloading

  • 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

#include <iostream>
using namespace std;


// function overloading
// Under the same scope
// Same function name
// The type, number or order of function parameters are different
void func(double a, int b) {
	cout << "10" << endl;
}
void func(string name) {
	cout << name << endl;
}
void func(int a, double b) {
	cout << "1010" << endl;
}


int main() {
	func("hello");
	func(1.0, 20);
	func(20, 1.0);
}

3.2 precautions

  • Reference as overload condition
  • Function overload encountered function default parameter
#include <iostream>
using namespace std;


// Precautions for function overloading
// 1. Reference as overload condition
void func(int& a) {
	cout << "func(int &a)call" << endl;
}
void func(const int& a) {
	cout << "func(const int &a)call" << endl;
}

// 2. The default parameters encountered by function overloading will be ambiguous
void func2(int a, int b = 10) {
	cout << "func(int a Default parameters)call" << endl;
}
void func2(int a) {
	cout << "func(int a)call" << endl;
}


int main() {
	const int a = 10;
	int b = 20;
	func(a);
	func(b);
	func2(a, b);
}

4, Classes and objects

Three characteristics of C + + object-oriented: encapsulation, inheritance and polymorphism

C + + believes that everything has attributes and behavior

1. Encapsulation

1.1 significance of packaging

Encapsulation is one of the three characteristics of C + + object-oriented

Meaning of encapsulation

  • Take attributes and behaviors as a whole to express things in life

    When designing classes, attributes and behaviors are written together to represent things

    Syntax: class name {access rights: attribute / behavior};

    // Set a circle class and find its circumference
    #include <iostream>
    using namespace std;
    
    // PI
    const double PI = 3.14;
    
    class Circle {
    	// Access rights: public rights
    public:  
    
    	// Attributes: radius
    	int m_r;  
    
    	// Behavior: get the circumference of a circle
    	double calcculate() {
    		return 2 * PI * m_r;
    	}
    };
    
    
    int main() {
    
    	// Create objects (instanced objects) through the circle class
    	Circle c1;
        
    	// Assign a value to the attribute of the circle object
    	c1.m_r = 10;
    
    	cout << "The circumference of the circle is" << c1.calcculate() << endl;
    }
    

    Case: design a student class with the attributes of name and student number. You can assign values to the name and student number, and display the student's name and student number

    #include <iostream>
    using namespace std;
    #include <string>;
    
    
    class Student
    {
    	// Access rights
    public:
    
    	// attribute
    	string stu_name;
    	int stu_id;
    
    	// behavior
    	void showInfo() {
    		cout << "full name:" << stu_name << " Student number:" << stu_id << endl;
    	}
    
    	// Assign values to attributes
    	void setInfo(string name, int id) {
    		stu_name = name;
    		stu_id = id;
    	}
    };
    
    
    int main() {
    	Student stu;  // instantiation 
    	stu.setInfo("Zhang San", 1);  
    	stu.showInfo();
    }
    

    The properties and behaviors in a class are called members

    Member attribute, member variable, member method / member function

  • Control attributes and behaviors with permission

    When designing classes, attributes and behaviors can be controlled under different permissions

    Access rights are

    • Public public permission
    • Protected protected permissions
    • Private private rights
    #include <iostream>
    using namespace std;
    #include <string>;
    
    
    // public members can be accessed inside the class or outside the class 
    // Protected members can be accessed inside the class, not outside the class, and sons can also access the contents protected by their fathers
    // Private members can be accessed within the class, not within the class, and sons cannot access the private content of their fathers
    
    class Person {
    	// Public authority
    public:
    	string name;
    	// Protection Authority
    protected:
    	string car;
    	// Private rights
    private:
    	int money;
    
    public:
    	void func() {
    		name = "zhansan";
    		car = "tuolaji";
    		money = 20;
    	}
    
    };
    int main() {
    	// Instantiate concrete objects
    	Person p1;
    	p1.name = "lishi";
    	// p1.car = "benc";  //  The protection permission cannot be accessed outside the class
    	// p1.money = 30;  //  Private permission content cannot be accessed outside the class
    }
    

1.2 differences between struct and class

In C + +, the only difference between struct and class is that the default access permissions are different

  • The default permission of struct is public
  • class default permission is private
#include <iostream>
using namespace std;
#include <string>;


class C1 {
	int A;  // The default permission is private
};
struct C2
{
	int A2;  // Default public
};
int main() {
	// struct default public
	C2 c2;
	c2.A2 = 100;  // Can access
	// class default private
	C1 c;
	// c.A = 100;  //  Cannot modify
}

1.3 privatization of member attributes

advantage

  • Set all member properties to private, and you can control the read and write permissions yourself
  • For write permission, we can check the validity of data
#include <iostream>
using namespace std;
#include <string>;


// Privatization of member properties
class Person {
public:
	// Set name and obtain permission
	void setName(string pname) {
		name = pname;
	}
	string getName() {
		return name;
	}

	/* // Get age
	int getAge() {
		age = 18;  // Initialize to 18 years old
		return age;
	} */
	// Readable and writable age, if you want to modify it (the age range must be 0 to 18 years old)
	int getAge() {
		return age;
	}
	// Set age
	void setAge(int num) {
		if (num < 0 || num > 18) {
			age = 15;
			cout << "There is a problem with the age you entered" << endl;
			return;
		}
		cout << num << endl;
		age = num;
	}


	// Write only property
	void setMoney(int num) {
		money = num;
	}



private:  // Set all private
	// The name is readable and writable
	string name;
	// Age read only
	int age; 
	// Property only write
	int money;
};


int main() {
	Person p;
	p.setName("Zhang San");
	cout << "name:" << p.getName() << endl;

	p.setAge(150);  // If the entered value does not meet the requirements, the assignment is directly forced
	cout << "Age:" << p.getAge() << endl;
}

1.4 cases

Design a Cube class (Cube), calculate the area and volume of the Cube, and judge whether the two cubes are equal by global function and member function respectively

#include <iostream>
using namespace std;


// Create a legislative class
class Cube {
	// Design properties
private:
	int c_l;  // long
	int c_h;  // high
	int c_w;  // wide

	// Behavior gets the area and volume of the cube
public:
	// set a property
	void setInfo(int l, int w, int h) {
		c_l = l;
		c_w = w;
		c_h = h;
	}
	// get attribute
	int getL() {
		return c_l;
	}
	int getH() {
		return c_h;
	}
	int getW() {
		return c_w;
	}
	// Get area
	int getS() {
		return c_l * c_h * 2 + c_l * c_w * 2 + c_h * c_w * 2;
	}
	// Get volume
	int getV() {
		return c_l * c_h * c_w;
	}
	// Member functions determine whether they are equal
	bool isSameByClass(Cube& c) {
		if (getH() == c.getH() && getW() == c.getW() && getL() == c.getL()) {
			return true;
		}
		return false;
	}

};

// Use global function and member function to judge whether two cubes are equal

// Global function
bool isSame(Cube &c1, Cube &c2) {  // Reference passing
	if (c1.getH() == c2.getH() && c1.getW() == c2.getW() && c1.getL() == c2.getL()) {
		return true;
	}
	return false;
}

int main() {
	// First cube
	Cube c1;
	c1.setInfo(1, 1, 1);
	int s1 = c1.getS();  // volume
	int v1 = c1.getV();  // the measure of area

	// Second cube
	Cube c2;
	c2.setInfo(1, 1, 2);
	int s2 = c2.getS();
	int v2 = c2.getV();
	
	if (isSame(c1, c2)) {
		cout << "identical" << endl;
	}
	else
	{
		cout << "inequality" << endl;
	}

	if (c1.isSameByClass(c2)) {
		cout << "Member judgment, same" << endl;
	}
	else
	{
		cout << "Member judgment, different" << endl;
	}
}

Judge the position of the point on the circle

#include <iostream>
using namespace std;

// Judge the relationship between point and circle
// Point class
class Point {
private:
	int x;
	int y;
public:
	void setPoint(int x, int y) {
		x = x;
		y = y;
	}
	int getx() {
		return x;
	}
	int gety() {
		return y;
	}
};


// Circular class
class Circle {
private:
	int r; // radius
	Point circlePoint;  // Represents the center of a circle
public:
	void setR(int r) {
		r = r;
	}
	int getR() {
		return r;
	}
	void setCenter(Point center) {
		circlePoint = center;
	}
	Point getCenter() {
		return circlePoint;
	}
};

// Judge the relationship between point and circle
void isInCircle(Circle &c, Point &p) {
	// Calculate the square of the distance
	if (pow(c.getCenter().getx() - p.getx(), 2) - pow(c.getCenter().gety() - p.gety(), 2) == pow(c.getR(), 2)) {
		cout << "Point on circle" << endl;
		return;
	}
	cout << "The point is not on the circle" << endl;
}


int main() {
	// Create circle
	Circle c;
	Point ct;
	ct.setPoint(10, 0);
	c.setCenter(ct);
	c.setR(10);
	Point p;
	p.setPoint(10, 10);
	isInCircle(c, p);
}

A class can instantiate another class as its attribute

2. Object initialization and cleanup

  • In life, the electronic products we buy basically have factory settings. One day, when we are not in use, we delete some of our own information data to ensure security
  • Object orientation in C + + comes from life. Each object has initialization settings and data cleaning settings before object destruction

2.1 constructors and analytic functions

Object initialization and cleanup are also two very important security issues

  • An object or variable has no initial state, and the consequences of its use are unknown
  • Similarly, after using an object or variable, it will also cause some security problems if it is not cleaned up in time

C + + uses constructors and destructors to solve the above problems. These two functions will be automatically called by the compiler to complete object initialization and cleaning

The initialization and cleanup of objects are what the compiler forces us to do, so if we don't provide constructors and destructors, the compiler will provide them

The constructor and destructor provided by the compiler are empty

  • Constructor: its main function is to assign value to the member attribute of the object when creating the object. The constructor is automatically called by the compiler without manual call
  • Destructor: the main function is that the system will automatically call and perform some cleaning work before the object is destroyed

Constructor syntax: class name () {}

  1. Constructor, no return value and no void
  2. The function name is the same as the class name
  3. Constructors can have arguments or overloads
  4. When the program calls the object, it will automatically call the construction without manual call, and it will only be called once

Destructor syntax: ~ class name () {}

  1. Destructor, no return value, no void
  2. The function name is the same as the class name, preceded by a symbol~
  3. Destructors cannot have parameters, so overloading cannot occur
  4. The program will automatically call the destructor before the object is destroyed. There is no need to call it manually, and it will only be called once
#include <iostream>
using namespace std;

class Person {
public:
	// Constructor
	Person() {
		cout << "Person Call of constructor" << endl;
	}

	// Destructor
	~Person() {
		cout << "Person Call of destructor" << endl;
	}

};
// Construction and deconstruction are necessary implementations. If we don't provide them ourselves, the compiler will provide the construction and Deconstruction of an empty implementation
void test() {
	Person p;  // The data on the stack will be automatically released after the test is executed
}
int main() {
	// Object initialization and cleanup
	//test();
	Person p;

	system("pause");
	return 0;
}

2.2 classification and calling of constructors

Two classification methods

  • Classification by parameters: parametric structure and nonparametric structure
  • Classification by construction and common copy type

Three call modes

  • bracketing
  • Display method
  • Implicit transformation method
#include <iostream>
using namespace std;

// Classification and calling of constructors
class Person {
public:
	int age;
	// Constructor
	// Nonparametric structure
	Person() {
		cout << "Person Call of constructor" << endl;
		age = 1;
	}
	// Parametric structure
	Person(int a) {
		age = a;
		cout << "Person Constructor calls have arguments" << a << endl;
	}
	// copy constructor 
	Person(const Person &p) {
		// Copy all the attributes of the incoming person to me
		age = p.age;
		cout << "Person Call copy of constructor" << age << endl;
	}
};
void test() {
	// call
	// bracketing
	Person p;  // Call of default constructor
	Person p02(10);  // Call parameterized constructor
	Person p03(p02);  //  Call of copy constructor 
	//Cout < < P2's age is: < < P2 age << endl;
	//Cout < < P3's age is: < < P3 age << endl;
	
	// Display method
	Person p1;
	Person p2 = Person(10);  // Parametric structure
	Person p3 = Person(p2);  // copy construction 
	//Person(10);  //  Anonymous object, features: after the execution of the current line, the system will immediately recycle the anonymous object

	// Implicit transformation method
	Person p4 = 10;  // Equivalent to Person p4 = Person(10);
	Person p5 = p4;
}
int main() {
	test();
}

matters needing attention

  • When calling the default constructor, do not add ()

    • Because of the following line of code, the compiler will consider it a declaration of a function

      Person p1();

  • Do not use copy constructors to initialize anonymous objects

    • Because the compiler will think that Person p4(p3) === Person p3;, Declaration that it is an object

      Person p4(p3);

2.3 call timing of copy function

The timing of copying function calls in C + + usually consists of three cases

  • Initialize a new object with an already created object
  • Value is passed to function parameters
  • Returns a local object as a value
#include <iostream>
using namespace std;


// Call timing of copy constructor

class Person {
public:
	Person() {
		cout << "Default function" << endl;
	}
	Person(int age) {
		age = age;
		cout << "Parametric function" << endl;
	}
	Person(const Person& p) {
		age = p.age;
		cout << "copying functions " << endl;
	}
	~Person() {
		cout << "Class is released" << endl;
	}
	int age;
};
void test01() {
// Initialize a new object with an already created object
	Person p1(20);
	Person p2(p1);
	cout << p2.age << endl;
}
void doWork( Person p ) {
	cout << "doWork" << endl;
}
void test02() {
// Value is passed to function parameters
	Person p;
	doWork(p);
}
Person doWork02() {
	Person p1;
	return p1;
}
void test03() {
// Returns a local object as a value
	Person p = doWork02();
}
int main() {
	//test01();
	//test02();
	test03();
	system("pause");
	return 0;
}

2.4 constructor calling rules

By default, the C + + compiler adds at least three functions to a class

  • Default constructor (no parameters, empty function body)
  • Default destructor (no parameters, empty function body)
  • The default copy function copies the values of attributes

Constructor call rule

  • If the user defines a parametric constructor, the default nonparametric constructor will not be provided in C + +, but the default copy constructor will be provided
  • If you define a copy constructor, C + + will not provide another constructor
#include <iostream>
using namespace std;


// Call rules for constructors

// 1. Create a class, and the C + + compiler will add at least three functions to each class, including destructor, default function and copy function

// If the user defines a parametric constructor, the default nonparametric constructor will not be provided in C + +, but the default copy constructor will be provided
class Person {
public:
	//Person() {
	//	Cout < < default function call < < endl;
	//}
	~Person() {
		cout << "Destructor call" << endl;
	}
	int age;
	Person(int age) {
		age = age;
		cout << age << endl;
	}
	//Person(const Person &p) {
	//	age = p.age;
	//	Cout < < copy constructor < < endl;
	// }
};
//void test1() {
//	Person p;
//	p.age = 18;
//	Person p2(p);
//	cout << p2. Age < < copy < < endl// Automatic call to copy function
//}
void test2() {
	Person p(10);
	Person p2(p);
	cout << p2.age << "Copy" << endl;
}
int main() {
	//test1();
	test2();
}

2.5 deep copy and light copy

Deep copy: a simple assignment copy operation

Shallow copy: re apply for space in the heap area for copy operation

#include <iostream>
using namespace std;


// Deep copy, shallow copy
class Person {
public:
	int age;
	int* height;
	Person() {
		cout << "Person Default constructor call for" << endl;
	}
	Person(int age_, int height_) {
		cout << "Person Parameterized constructor call" << endl;
		age = age_;
		height = new int(height_);  // Create height data into the heap area
	}
	Person(const Person& p) {
		cout << "Person Copy constructor call" << endl;
		age = p.age;
		// height = p.height;  //  The compiler implements this line of code by default -- a shallow copy
		// Deep copy operation
		height = new int(*p.height);
	}
	~Person() {
		// Open up the operation area of code analysis and release the data
		if (height != NULL) {
			delete height;
			height = NULL;  // Avoid wild pointers
		}
		cout << "Person Destructor call" << endl;
	}
};
void test() {
	Person p1(18, 160);
	cout << "p1 Your age is" << p1.age << "  p1 Your height is" << *p1.height <<endl;
	Person p2(p1);  // If you use the copy constructor provided by the compiler, you will do a shallow copy operation; The problem with shallow copy is that the memory in the heap will be released repeatedly
	// This problem needs to be solved by deep copy
	cout << "p2 Your age is" << p2.age << "  p2 Your height is" << *p2.height <<endl;
}
int main() {
	test();
	system("pause");
	return 0;
}

2.6 initialization list

effect

  • C + + provides initialization list syntax to initialize attributes

Syntax: constructor (): Property 1 (value 1) property 2 (value 2) {}

#include <iostream>
using namespace std;


// Initialization list
class Person {
public:
	// Traditional initialization operation: use the parameter passing constructor to operate

	// Initialization list initialization properties
	// Person() :a(10), b(20), c(30) {
	// }
	// More flexible initialization
	Person(int a, int b, int c) :a(a), b(b), c(c) {
	}
	int a;
	int b;
	int c;
};
void test() {
	Person p(20, 30, 50);
	cout << "establish P" << endl;
	cout << p.a << p.b << p.c << endl;
}
int main() {
	test();
	system("pause");
	return 0;
}

2.7 class objects as class members

A member in a C + + class can be an object of another class, which we call an object member

class A {}
class B {
    A a;  // Class B has object A as A member and A as an object member
}

Then, when creating A B object, which is the construction and deconstruction order of A and B?

#include <iostream>
using namespace std;


// Class object as class member
class Phone {
public:
	string c_Pname;
	Phone(string pName) {
		c_Pname = pName;
		cout << "phone Constructor" << endl;
	}
	~Phone() {
		cout << "Phone Destructor" << endl;
	}
};
class Person {
public:
	string c_Name;
	Phone c_Phone;
	Person(string name, string pName): c_Name(name), c_Phone(pName) {
		cout << "person Constructor" << endl;
	}
	~Person() {
		cout << "person Destructor" << endl;
	}
};
void test() {
	Person p("Zhang San", "Fruit 13 ProMax");
	cout << "c_Name:" << p.c_Name << "  c_Phone:" << p.c_Phone.c_Pname << endl;
}
int main() {
	test();
	system("pause");
	return 0;
}

The order of construction is: first call the construction of object members, and then call the construction of this class

The order of deconstruction is opposite

2.8 static members

Static members are called static members by adding the keyword static before member variables and member functions

Static members are divided into

  • Static member variable
    • All objects share the same data
    • Allocate memory at compile time
    • In class declaration, out of class initialization
  • Static member function
    • All objects share the same function
    • Static member functions can only access static member variables
#include <iostream>
using namespace std;

// Static member function
class Person {
public:
	// Static member function
	static void func() {
		c_A = 100;
		// c_B = 100;  //  Static member functions cannot access non static member variables
		cout << "static void func" << c_A << endl;
	}
	static int c_A;  // Static member variable, class declaration
	int c_B;  // Non static member variable
};
int Person::c_A = 10;  // Initialization outside class
void test() {
	// visit
	Person p;
	p.func();  // Access by object
	
	Person::func();  // Access through class name:: is the domain operator
}
int main() {
	test();
	system("pause");
	return 0;
}

Static functions also have access

3. C + + object model and this pointer

3.1 member variables and member functions are stored separately

In C + +, in class member variables and member functions are stored separately

Only non static member variables are objects of a class

#include <iostream>
using namespace std;

// Member variables and member functions are stored separately
class Person {
	int c_A;  // Non static member variable, object belonging to class
	static int c_B;  // Static member variable, which does not belong to class object
	void fn() {};  // Non static member function, which does not belong to class object
	static void  fn() {};  // Static member function, object that does not belong to class
};

void test01() {
	Person  p;
	/*
	The memory space occupied by empty objects is 1
	 C++ The compiler will also allocate a byte space to each empty object to distinguish the memory occupied by empty objects
	Each empty object should also have a unique memory address
	 */
	cout << sizeof(p) << endl;
}

int main() {
	test01();

	system("pause");
	return 0;
}

3.2 this pointer

Each non static member function will only produce one function instance, that is, multiple objects of the same type will share a piece of code

C + + determines whether this piece of code is called by an object by providing a special object pointer and this pointer.

this pointer points to the object to which the called member function belongs

this pointer is a pointer implied in every non static member function

this pointer does not need to be defined and can be used directly

purpose

  • When a formal parameter has the same name as a member variable, it can be distinguished by this pointer
  • Return the object itself in the non static member function of the class. You can use return *this
#include <iostream>
using namespace std;

class Person {
public:
	Person(int age) {
		this->c_age = age;  // The this pointer points to the object to which the called member function belongs
	}
	int c_age;

	Person& addAge(Person &p) {  // Person & a reference that returns a value. If a value is returned, a new object will be created
		this->c_age += p.c_age;
		return *this;  // This refers to the pointer of the object calling the function, so * this refers to the ontology of the object 
	}
};

void test01() {
	Person p1(18);
	cout << "p1 Your age is" << p1.c_age << endl;
}
void test02() {
	Person p1(18);
	Person p2(19);

	// Chain programming idea
	p2.addAge(p1).addAge(p1);
	cout << "p2 Your age is" << p2.c_age << endl;
}

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

	system("pause");
	return 0;
}

3.3 null pointer access member function

C + + hollow pointer can also call member functions, but pay attention to whether there is a useful this pointer

If this pointer is used, it needs to be judged to ensure the robustness of the code

#include <iostream>
using namespace std;


class Person {
public:
	void showClassName() {
		cout << "this is Person class" << endl;
	}

	void showPersonAge() {
		// The reason for the error is that the pointer passed in is NULL
		if (this == NULL) {
			return;
		}
		cout << "age=" << c_Age << endl;
	}
	int c_Age;
};

void test01() {
	Person* p = NULL;

	p->showClassName();
	//p->showPersonAge();
}
int main() {
	test01();
	system("pause");
	return 0;
} 

3.4 const modifier member function

Constant function

  • After adding const to the member function, we call this function a constant function
  • Member properties cannot be modified within a constant function
  • After the keyword mutable is added to the member attribute declaration, it can still be modified in the constant function

Constant object

  • Add const before declaring the function to call the object a constant object
  • Constant objects can only call constant functions
// Constant function
class Person {
public:

	// The essence of this pointer is a pointer constant, and the pointer cannot be modified
	void showPerson() const {  // ->Equivalent to const Person * const this; Originally, it was Person * const this; 
		// m_A = 100;  //  Will report an error
		this->m_B = 100;
	}

	int m_A;
	mutable int m_B;  // For special variables, even in constant functions, you can modify this value and add the keyword mutable
};

// Constant object
void test() {
	const Person p;  // It is a constant object
	// p.m_A = 100;  //  Modification not allowed
	p.m_B = 100;  // m_B is a special value, which can also be modified under constant objects
	// Constant objects can only call constant functions, which cannot call ordinary member functions, because ordinary member functions can modify properties
}

4. Friends

In the program, some private properties also want to be accessed by special functions or classes outside the class, so you need to use friend technology

The purpose of a friend is to let a function or class access private members in another class

The keyword of friend is: friend

Three implementations of friends

  1. Global function as friend
  2. Class as friend
  3. Member function as friend

4.1 global functions

// In the class, add an object declaration. At the same time, add friend at the beginning
// Global function as friend
class Building {
	// goodGay global function is a good friend of Building. You can access private members in Building
	friend void goodGay(Building* building);

public:
	Building() {
		m_Bedroom = "bedroom";
		m_Sittingroom = "a living room";
	}

public:
	string m_Sittingroom;  

private:
	string m_Bedroom;
};

// Global function
void goodGay(Building* building) {
	cout << "Good friends visit" << building->m_Sittingroom << endl;
	cout << "Good friends visit" << building->m_Bedroom << endl;
}
void test01() {
	Building building;
	goodGay(&building);

Category 4.2

// Class as friend
class Building {
	// Friends
	friend class GoodGay;

public:
	Building();

public:
	string m_Sittingroom;  

private:
	string m_Bedroom;
};
// Class write out member function
Building::Building() {
	m_Bedroom = "bedroom";
	m_Sittingroom = "a living room";
}
// class
class GoodGay {
public:
	GoodGay();

	void visitHouse();

	Building* building;
};
// Class write out member function
GoodGay::GoodGay() {
	// Create a building object
	building = new Building;
}
void GoodGay::visitHouse() {
	cout << "A good friend is visiting" << building->m_Sittingroom << endl;
	cout << "A good friend is visiting" << building->m_Bedroom << endl;
}

void test() {
	GoodGay gg;
	gg.visitHouse();
}

4.3 member functions

class Building;  // Class declaration
class GoodGay {
public:
	GoodGay();

	void visit();  // Enables functions to access private members in Building
	void visit1();  // Make private members inaccessible to functions
	Building* building;
};
class Building {
	friend void GoodGay::visit();  // Use domain operators
public:
	Building();
	string m_Sitttingroom;
private:
	string m_Bedroom;
};
// Implement member functions outside the class
GoodGay::GoodGay() {
	building = new Building;
}
void GoodGay::visit() {
	cout << "Visiting" << building->m_Sitttingroom << endl;
	cout << "Visiting" << building->m_Bedroom << endl;
}
void GoodGay::visit1() {
	cout << "Visiting" << building->m_Sitttingroom << endl;
	// Cout < < visiting "< < building - > m_ Bedroom << endl;  //  report errors
}
Building::Building() {
	m_Sitttingroom = "a living room";
	m_Bedroom = "bedroom";
}

void test() {
	GoodGay gg;
	gg.visit();
	cout << "---------"<< endl;
	gg.visit1();
}

5. Operator overloading

Operator overloading concept: redefine the existing operators and give them another function to adapt to different data types

5.1 plus operator

Function: realize the operation of adding two user-defined data types

// Plus operator overload
class Person {
public:
	// Member overloading 
	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;
};
// Global function overloading
Person operator+(Person& p1, Person& p2) {
	Person temp;
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}
void test() {
	Person p1;
	p1.m_A = 10;
	p1.m_B = 10;
	Person p2;
	p2.m_A = 10;
	p2.m_B = 10;

    // Essential call of member function
    // Person p3 = p1.operator+(p2);
    // Essential call of global function
    // Person p3 = operator+(p1, p2);
	Person p3 = p1 + p2;
	cout << "p3.m_A: " << p3.m_A << "\tp3.m_B: " << p3.m_B << endl;
}

Operator overloading can also occur with function overloading

Person operator+(Person& p1, int num) {
	Person temp;
	temp.m_A = p1.m_A + num;
	temp.m_B = p1.m_B + num;
	return temp;
}  // Number addition

The operators of expressions with built-in data types cannot be changed

Do not abuse operator overloading

5.2 shift left operator

Function: you can output custom data types

// Overloaded operator shift left
class Person
{
public:
	int m_A;
	int m_B;
	/* 
	Overloading with member functions: p.operator < < (cout); - > Simplified function P < < cout;
	The < < operator will not be overloaded with member functions because cout cannot be realized on the left
	void operator<< (ostream& cout)
	{
		cout << "a:" << m_A << " b:" << m_B << endl;
	} 
	*/
};
void test()
{
	Person p;
	p.m_A = 10;
	p.m_B = 10;
	cout << p << endl;
}
// You can only use global function overloading, ostream, which belongs to the standard output stream
ostream& operator<< (ostream& cout, Person p)  // Essence: operator < < (cout, P); - > Simplify cout < < p;
{
	cout << "a:" << p.m_A << " b:" << p.m_B;
	return cout;  // Chain programming should have return value
}

Conclusion: overloading the shift left operator and cooperating with friends can output user-defined data types

5.3 increment operator

Function: realize your own integer data by overloading the increment operator

// Overload autoincrement operator

// Custom integer
class MyInt
{
	friend ostream& operator<<(ostream& cout, MyInt i);  // Friend access private element
public:
	MyInt()
	{
		m_num = 0;
	}
	// Overloaded leading autoincrement operator
	MyInt& operator++()
	{
		++m_num;  // Auto increment operation
		return *this;
	}
	// Overloaded post increment operator
	MyInt operator++(int)  // int represents a placeholder parameter, which is used to distinguish between pre increment and post increment. Be careful not to use references
	{
		// Record the results at that time first
		MyInt temp = *this;
		// Post self increasing
		m_num++;
		// Finally, return the result
		return temp;
	}
private:
	int m_num;
};
// Overload shift left operator
ostream& operator<<(ostream& cout, MyInt i)
{
	cout << i.m_num;
	return cout;
}
void test()
{
	MyInt i;
	cout << ++i << "--" << i << endl;
	cout << "=====" << endl;
	cout << i++ << "--" << i << endl;
}

5.4 assignment operator

The C + + compiler adds at least four functions to a class

  1. Default constructor (no parameters, empty function body)
  2. Default destructor (no parameters, empty function body)
  3. The default copy function copies the values of attributes
  4. The assignment operator operator =, copies the value of the attribute

If there is an attribute pointing to the heap area in the, the deep and shallow copy problem will also occur when performing the assignment operation

class Person
{
public:
	Person(int age)
	{
		m_Age = new int(age);  // Open up data to heap
	}
	// Overload assignment operator
	Person& operator=(Person& p)
	{
		// The compiler provides a shallow copy of this - > M_ Age = p.m_ Age;
		// You should judge whether there are attributes in the heap area. If so, release them clean first, and then deep copy them
		if (this->m_Age)  // Equivalent to if (this - > m_age! = null)
		{
			delete this->m_Age;  // Delete data
			m_Age = NULL;
		}
		m_Age = new int(*p.m_Age);
		// Returns the object itself
		return *this;
	}
	int* m_Age;
	~Person()
	{
		if (m_Age)  // When m_ When age is not empty
		{
			delete m_Age;
			m_Age = NULL;
		}
	}
};
void test()
{
	Person p1(10);
	Person p2(20);
	p2 = p1;  // Assignment operation, default to light copy
	cout << "p1 Your age is:" << *p1.m_Age << endl;
	cout << "p2 Your age is:" << *p2.m_Age << endl;
}

5.5 relational operators

Function: overloads the relational operator, allowing two user-defined objects to compare

// Overloading relational operators
class Person
{
public:
	Person(string name, int age);
	// Overload = = sign, others are similar
	bool operator==(Person& p); 
	string m_Name;
	int m_Age;
};
Person::Person(string name, int age)
{
	this->m_Name = name;
	this->m_Age = age;
}
bool Person::operator==(Person& p)
{
	if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
	{
		return true;
	}
	else
	{
		return false;
	}
}
void test()
{
	Person p1("Tom", 18);
	Person p2("Tom", 18);
	Person p3("Li Hua", 18);
	if (p1 == p2)
	{
		cout << "p1 == p2" << endl;
	}
	else
	{
		cout << "p1 != p2" << endl;
	}
}

5.6 function call operator

  • The function call operator () can also be overloaded
  • Because the method used after overloading is very similar to the call of function, it is called imitation function
  • Imitation function has no fixed writing method and is very flexible
class MyPrint
{
public:
	void operator()(string test)
	{
		cout << test << endl;
	}
};
class Add
{
public:
	int operator()(int num1, int num2)
	{
		return num1 + num2;
	}
};
void test()
{
	MyPrint myPrint;
	myPrint("hello world");  // functor 

	Add add;
	cout << "And are:" << add(1, 2) << endl;  // Imitation function is very flexible and has no fixed writing method
	cout << "And are:" << Add()(2, 3) << endl;  // Anonymous function object ` Add()`
}

6. Inherit

Inheritance is one of the three characteristics of object-oriented

There are special relationships between some classes

For example: Animal - > cat - > Garfield

We found that when defining these classes, the lower level members not only have the commonalities of the upper level, but also have their own attributes

At this time, we can consider using inheritance technology to reduce duplicate code

6.1 basic grammar

Syntax: class subclass: inheritance method parent class

For example, in many websites, there are public headers, public bottoms, and even public left list columns

/*
class Java
{
public:
	Java()  // Use the constructor to output the content
	{
		cout << "Java Page: "< endl;
		header();
		buttom();
		left();
		content();
		cout << "=====" << endl;
	}
	void header()
	{
		cout << "Home page, open class, login, registration " << endl;
	}
	void buttom()
	{
		cout << "Help center, communication and cooperation, site map " << endl;
	}
	void left()
	{
		cout << "Java / Python / C++ ..." << endl;
	}
	void content()
	{
		cout << "Course on Java "< < endl;
	}
};
class Python  // Much of the content is the same as that of Java
{
public:
	Python()  
	{
		cout << "Python Page: "< endl;
		header();
		buttom();
		left();
		content();
		cout << "=====" << endl;
	}
	void header()
	{
		cout << "Home page, open class, login, registration " << endl;
	}
	void buttom()
	{
		cout << "Help center, communication and cooperation, site map " << endl;
	}
	void left()
	{
		cout << "Java / Python / C++ ..." << endl;
	}
	void content()
	{
		cout << "Course on Python "< < endl;
	}
};
// There are many function repetitions
*/
// Using inheritance ideas
class PublicPage  // Define public pages
{
public:
	void header()
	{
		cout << "Home page, open class, login, registration..." << endl;
	}
	void buttom()
	{
		cout << "Help center, communication and cooperation, station map..." << endl;
	}
	void left()
	{
		cout << "Java / Python / C++ ..." << endl;
	}
};
class Java : public PublicPage
{
public:
	Java()
	{
		cout << "Java Page:" << endl;
		header();
		buttom();
		left();
		content();
		cout << "=====" << endl;
	}
	void content()
	{
		cout << "about Java Courses" << endl;
	}

};
class Python : public PublicPage
{
public:
	Python()
	{
		cout << "Python Page:" << endl;
		header();
		buttom();
		left();
		content();
		cout << "=====" << endl;
	}
	void content()
	{
		cout << "about Python Courses" << endl;
	}

};
void test()
{
	Java java;
	Python python;
}

Subclasses are also called derived classes

A parent class is also called a base class

6.2 mode of inheritance

Inheriting content that cannot access private permissions

There are three ways of inheritance

  • Public succession

    • All public members in the base class are public attributes in the derived class
    • All protected members in the base class are protected attributes in the derived class
    • All private members in a base class cannot be used in a derived class
  • Protect inheritance

    • All public members in the base class are protected in the derived class
    • All protected members in the base class are protected attributes in the derived class
    • All private members in a base class cannot be used in a derived class.
  • Private inheritance

    • All public members in the base class are private attributes in the derived class;
    • All protected members in the base class are private attributes in the derived class
    • All private members in a base class cannot be used in a derived class
Inheritance method / base class member public member protected member private member
public inheritance public protected invisible
protected inheritance protected protected invisible
private inheritance private private invisible

6.3 object model

Which members inherited from the parent class belong to the subclass objects?

// Object model in inheritance
class Base
{
public:
	int m_A;
private:
	int m_B;
protected:
	int m_C;
};
class Son : public Base
{
public:
	int m_D;
};
void test()
{
	cout << "size of son:" << sizeof(Son) << endl;
	// In the parent class, all non static member properties will be inherited by the child class
	// The private member attribute in the parent class is hidden by the compiler, so it is inaccessible, but it is inherited
}

Use the developer command prompt tool to view the object model

  1. Jump drive letter
  2. Jump file path: cd specific path
  3. View command: cl /d1 reportSingleClassLayout class name file name

6.4 structure and destructional sequence

After the subclass inherits from the parent class, when the subclass object is created, the constructor of the parent class will also be called

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

};
void test()
{
	Son son;  // Dolls, white haired people give black haired people
}

6.5 handling of members with the same name

When there are members with the same name in the subclass and parent class, how can you access the data with the same name in the subclass or parent class through the subclass object?

  • Access subclass members with the same name: direct access
  • Accessing a member with the same name as the parent class: scope is required
class Base
{
public:
	Base()
	{
		m_A = 100;
	}
	int m_A;
	void fn() 
	{
		cout << "Base" << endl;
	}
    void fn(int a)
    {
        cout << a << endl;
    }

};
class Son : public Base
{
public:
	Son()
	{
		m_A = 101;
	}
	int m_A;
	void fn()
	{
		cout << "Son" << endl;
	}
};
void test()
{
	Son s;
	cout << "s.m_A=" << s.m_A << endl;  // Direct access
	cout << "Base.m_A=" << s.Base::m_A << endl;  // Add scope
	s.fn();
	s.Base::fn();
    s.Base::fn(100);
}

The method of calling a function with the same name is similar: add scope

If a member function with the same name as the parent class appears in the subclass, the member function with the same name in the subclass will hide all member functions with the same name in the parent class

If you want to access the member function with the same name hidden in the parent class, you need to add scope

6.6 processing of static members with the same name

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
class Base
{
public:
	static int m_A;  // Intra class declaration
	static void fn()
	{
		cout << "Base" << endl;
	}
};
int Base::m_A = 100;  // Class to initialize
class Son : public Base
{
public:
	static int m_A;
	static void fn()
	{
		cout << "Son" << endl;
	}
};
int Son::m_A = 101;

void test() 
{
	cout << "Access data by class name" << endl;
	Son s;
	cout << "s.m_A=" << s.m_A << endl;
	cout << "Base.m_A=" << s.Base::m_A << endl;

	cout << "Access data by class name" << endl;
	cout << "Son.m_A=" << Son::m_A << endl;
	cout << "Base.m_A=" << Son::Base::m_A << endl; 
	// The first pair of colons represents accessing the scope through the class name, and the second pair of colons represents accessing the scope under the parent class
}

Function calls are similar

6.7 multi inheritance syntax

C + + allows a class to inherit 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 for C + + actual development

6.8 diamond inheritance

problem

  1. When B inherits the data of A and C also inherits the data of A, ambiguity will occur when D uses the data
  2. D inherits two data. In fact, we know that we only need to inherit one data
class A
{
public:
	int m_Age;
};
/* 
Using virtual inheritance to solve the problem of diamond inheritance
 Before inheritance, add the keyword virtual to become virtual inheritance
A Classes are called virtual base classes
*/
class B : virtual public A{};
class C : virtual public A{};
class D : public B, public C{};
void test()
{
	D d;
	d.B::m_Age = 18;
	d.C::m_Age = 28;
	// When diamond inheritance occurs, the two parent classes have the same data and need to be distinguished
	cout << "B.m_Age = " << d.B::m_Age << endl;
	cout << "C.m_Age = " << d.C::m_Age << endl;
	// This data only needs one data. Diamond inheritance leads to two data, which is a waste of resources
}

Using virtual inheritance to solve the problem of diamond inheritance
Before inheritance, add the keyword virtual to become virtual inheritance
Class A is called a virtual base class

7. Polymorphism

7.1 basic concepts

Polymorphism is one of the three characteristics of C + + object-oriented

Polymorphisms fall into two categories

  • Static polymorphism: [function overload] (#3, function overload) and [operator overload] (#5, operator overload) belong to static polymorphism and reuse function names
  • Dynamic polymorphism: derived classes and virtual functions implement runtime polymorphism

The difference between static polymorphism and dynamic polymorphism

  • Statically polymorphic function address early binding - function address at compilation stage
  • Dynamic polymorphic function address late binding - determine the function address at the run-time
class A
{
public:
	virtual void speak()  // Virtual function, which can realize late address binding
	{
		cout << "A is speaking" << endl;
	}
	
};
class B : public A 
{
public:
	void speak()
	{
		cout << "B is speaking" << endl;
	}
};
// Execute speaking function
void doSpeak(A& a)  // A& a = b;
{
	a.speak();  // If virtual is not added, the address is bound early and the function address is determined at the compilation stage
	// If you want to execute b.speak(); Binding is required in the running phase, and the address is bound late
}
void test()
{
	B b;
	doSpeak(b);
}

Rewrite function: the return value type, function name and parameter list of the function are exactly the same

Dynamic condition satisfaction

  • There is an inheritance relationship
  • Subclass overrides the virtual function of the parent class, and the subclass override function can not be a virtual function

Dynamic polymorphic use

  • The parent class pointer or reference executes the child class object

Polymorphic advantage

  • Clear code organization
  • Strong readability
  • It is conducive to the expansion and maintenance in the early and later stages

7.2 case - calculator

// If there is an extension function, the opening and closing principle should be realized: open the extension and close the modification
class AbstarctCalc  // abstract class
{
public:
	virtual int getResult()  // virtual function
	{
		return 0;
	}
	int m_A;
	int m_B;
};
class Add : public AbstarctCalc  // addition
{
public:
	int getResult()
	{
		return m_A + m_B;
	}
};
class Sub : public AbstarctCalc
{
public:
	int getResult()
	{
		return m_A - m_B;
	}
};
void test()
{
	// Polymorphic service conditions
	// A parent class pointer or reference points to a child class object
	AbstarctCalc* abc = new Add;
	abc->m_A = 10;
	abc->m_B = 20;
	cout << abc->m_A << " + " << abc->m_B << " = " << abc->getResult() << endl;  // Execute subclass object
	delete abc;  // Destroy data
	
	abc = new Sub;  
	abc->m_A = 10;
	abc->m_B = 20;
	cout << abc->m_A << " - " << abc->m_B << " = " << abc->getResult() << endl; 
}

7.3 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 to 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

Characteristics of abstract classes

  • Cannot implement instantiated object
  • Subclasses must override pure virtual functions in abstract classes, otherwise they also belong to abstract classes
class Base
{
public:
	virtual void func() = 0;
};
class Son : public Base
{
public:
	void func()
	{
		cout << "Son" << endl;
	}
};
void test(Base& b)  // Reference parent class pointer
{
	b.func();
}
void test()
{
	Son s;
	test(s);
}

7.4 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

Analysis of virtual and pure commonness

  • You can solve the problem of releasing subclass objects from parent class pointers
  • All need specific function implementation

The 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: class interior: virtual ~ class name () = 0| Out of class: Class Name:: ~ class name () {}

// Virtual destruct and pure virtual destruct
class A
{
public:
	A()
	{
		cout << "A()" << endl;
	}
	virtual void speak() = 0;
	virtual ~A()
	{
		cout << "~A()" << endl;
	}
	// Pure virtual destruct virtual ~A() = 0;
};
// A::~A() {} / / pure virtual destructor requires function implementation
class C : public A
{
public:
	C(string name)
	{
		cout << "C()" << endl;
		m_name = new string(name);  // Open up data to heap
	}
	void speak()
	{
		cout << *m_name << " is speaking" << endl;
	}
	string* m_name;  // This destructor is not called when virtual is not added
	~C()
	{
		if (m_name)
		{
			cout << "~C()" << endl;
			delete m_name;
			m_name = NULL;
		}
	}
};
void test()
{
	A* a = new C("Tom");
	a->speak();
	// When the parent class pointer is destructed, it will not call the destructor in the subclass, resulting in a waste of resources if the subclass has heap attributes
	delete a;
}

Virtual destructors or pure virtual destructors are used to solve the problem of releasing subclass objects through parent class pointers

If no data in the subclass is opened to the heap area, it can not be written as virtual destruct or pure virtual destruct

Classes with pure virtual destructors are also abstract classes

5, File operation

The data generated when the program runs belongs to temporary data. Once the program runs, it will be released

Data can be stored persistently through files

File operations in C + + need to include header files < fstream >

  1. Text file: the file is stored in the computer in the form of ASCII code of text
  2. Binary file: the file is stored in the computer in the binary form of text, which is generally not directly readable by users

Operation documents are divided into three categories

  1. ofstream: write operation
  2. ifstream: read operation
  3. fstream: read / write operation

1. Text file

1.1 writing documents

1.1.1 steps

  1. Include header file

    • #include <fstream>
      
  2. Create flow object

    • ofstream ofs;
      
  3. Open file

    • ofs.open("File path", Open mode);
      
  4. Write data

    • ofs << "Data written";
      
  5. Close file

    • ofs.close();
      

1.1.2 file opening method

File open mode flag
Pattern mark Applicable object effect
ios::in ifstream
fstream
Open the file for reading data. If the file does not exist, there is an error opening it.
ios::out ofstream
fstream
Open file for writing data. If the file does not exist, create a new one; If the file already exists, the original contents will be cleared when it is opened.
ios::app ofstream
fstream
Open a file to add data to its tail. If the file does not exist, create a new one.
ios::ate ifstream Open an existing file and point the file read pointer to the end of the file (the concept of read-write refers will be explained later). If the file does not exist, there is an error opening it.
ios:: trunc ofstream When the file is opened, all the data stored inside will be emptied. When used alone, it is the same as ios::out.
ios::binary ifstream
ofstream
fstream
Open the file in binary mode. If this mode is not specified, it is opened in text mode.
ios::in | ios::out fstream Open an existing file to read its contents or write data to it. When the file is first opened, the original content remains unchanged. If the file does not exist, there is an error opening it.
ios::in | ios::out ofstream Open files to which data can be written. When the file is first opened, the original content remains unchanged. If the file does not exist, there is an error opening it.
ios::in | ios::out | ios::trunc fstream When you open a file, you can read its contents or write data to it. If the file already exists, clear the original content when opening it; If the file does not exist, create a new one.

The file opening method can be used with |

1.2 document reading

The steps of reading and writing files are similar, but there are relatively many reading methods

  1. Include header file

    • #include <fstream>
      
  2. Create flow object

    • ifstream ifs;
      
  3. Open file

    • ifs.open("File path", Open mode);
      if (!ifs.is_open())
      {
          cout << File open failed << endl;
          return;
      }  // ifs.is_open() failed to judge whether the file is opened
      
  4. Read data

    • // Four ways to read
      // First kind
      char buf[1024] = { 0 };  // Initialize character array
      while (ifs >> buf)
      {
          cout << buf << endl;
      }
      // Second
      char buf[1024] = { 0 };  // Initialize character array
      while (ifs.getline(buf, sizeof(buf)))
      {
          coutt << buf << endl;
      }
      // Third
      string buf;
      while (getline(ifs, buf))
      {
          cout << buf << endl;
      }
      // Fourth (not recommended)
      char c;
      while (c = ifs.get() != EOF)  // End of EOF file
      {
          cout << c;
      }
      
  5. Close file

    • ifs.close();
      

2. Binary file

Read and write files in binary mode

The opening method is ios::binary

2.1 writing documents

Writing files in binary mode mainly uses the stream object to call the member function write

Syntax: ostream & write (const char * buffer, int len);

parameter

  • buffer: character pointer pointing to a storage space in memory
  • len: number of bytes read and written
class Person
{
public:
	char m_Name[64];
	int m_Age;
};
// Include header file
// #include <fstream>

// Create flow object
ofstream ofs;

// Open file
ofs.open("Person.txt", ios::binary | ios::out);
// You can also: ofstream ofs("Person.txt", ios::binary | ios::out);

// Write file
Person p = { "Zhang San", 18 };
ofs.write((const char*)&p, sizeof(Person));

// Close file
ofs.close();

2.2 reading documents

Binary file reading mainly uses the stream object to call the member function read

Syntax: istream & read (char * buffer, int len)

parameter

  • buffer: character pointer pointing to a storage space in memory
  • len: number of bytes read and written
class Person
{
    public:
    char m_Name[64];
    int m_Age;
};
// Include header file #include < fstream >

// Create flow object
ifstream ifs;
// Open the file and judge whether the file is opened successfully
ifs.open("Person.txt", ios::in | ios::binary);  
if (!ifs.is_open())
{
    cout << "File open failed" << endl;
    return;
}
// read file
Person p;
ifs.read((char*)&p, sizeof(Person));
cout << "full name:" << p.m_Name << " Age:" << p.m_Age << endl;

// Close file
ifs.close();

Keywords: C++ Back-end

Added by dcinadr on Mon, 14 Feb 2022 06:46:58 +0200