C++ core programming notes, with code

 

Table of contents

1. Memory partition model

 1.1 Before the program runs

code area:

Global zone: 

1.2 After the program runs 

Stack area:

Heap area:

1.3 new operator

2. Quote

2.1 Basic use of references 

2.2 Notes on citations

2.3 References as function parameters

2.4 Reference as function return value

 2.5 The nature of citations

2.6 Constant reference

3. Function improvement

3.1 Function default parameters 

3.2 Placeholder parameters of functions

3.3 Function overloading

 3.3.1 Overview of function overloading

 3.3.2 Notes on function overloading

4. Classes and Objects

4.1 Packaging 

4.1.1 Significance of encapsulation 

 4.1.2 The difference between struct and class

 4.1.3 Member attributes are set to private

Exercise Case 1: Designing a Cube Class

 Exercise Case 2: The relationship between points and circles

 class split

 4.2 Object initialization and cleanup

 4.2.1 Constructor and destructor

4.2.2 Classification and call of constructor

4.2.3 When to call the copy constructor

4.2.4 Constructor calling rules

 4.2.5 Deep Copy and Shallow Copy

4.2.6 Initialization List

4.2.7 Class objects as class members

 4.2.8 Static members

4.3 C++ object model and this pointer

4.3.1 Member variables and member functions are stored separately 

4.3.2 The concept of this pointer

4.3.3 Null pointer access to member functions

 4.3.4cosnt modified member function 

4.4 Tomomoto

4.5 Operator overloading

4.5.1 Plus Operator Overloading

4.5.2 Left shift operator overloading

4.5.3 Increment operator overloading

 4.5.4 Assignment operator overloading

 4.5.5 Relational operator overloading

4.5.6 Function call operator overloading

4.6 Inheritance

4.6.1 Basic Syntax of Inheritance 

4.6.2 Inheritance method

 4.6.3 Object Models in Inheritance

4.6.4 Construction and Destruction Order in Inheritance

4.6.5 How to Inherit Members with the Same Name

4.6.6 Inheritance of static members with the same name processing method

 4.6.7 Multiple inheritance syntax

 4.6.8 Diamond Inheritance

4.7 Polymorphism

 4.7.1 Basic Concepts of Polymorphism

 4.7.2 Polymorphic Case 1 Calculator Class

4.7.3 Pure virtual functions and abstract classes 

 4.7.5 Virtual and pure virtual destruction

5. File operation 

5.1 Document text 

 5.1.1 Writing files

5.1.2 Reading files

 5.2 Binaries

5.2.1 Writing files 

5.2.2 Reading files


This stage mainly focuses on the detailed explanation of C++ object-oriented programming technology, and discusses the core and essence of C++

1. Memory partition model

When the C++ program is executed, the general direction of memory is divided into 4 areas

  • Code area: store the binary code of the function body, managed by the operating system
  • Global area: store global variables and static variables and constants
  • Stack area: automatically allocated and released by the compiler, storing function parameter values, local variables, etc.
  • Heap area: allocated and released by the programmer, if the programmer does not release it, it will be reclaimed by the operating system at the end of the program 

The meaning of the four memory areas:

        The data stored in different areas are endowed with different life cycles, giving us more flexible programming 

 1.1 Before the program runs

After compiling and compiling, an exe executable program is generated, which is divided into two areas before the program is executed

code area :

        Stores machine instructions executed by the CPU

        The code area is shared . The purpose of sharing is that for programs that are frequently executed, only one copy of the code is required in the memory.

        The code area is read-only . The reason for making it read-only is to prevent the program from accidentally modifying its instructions. 

Global zone

        Global variables and static variables are stored here

        The global area also includes the constant area , where string constants and other constants are also stored

         The data in this area is released by the operating system after the program ends

 Local variables and const-modified local variables are not in the global area

The global area has global variables, static variables, and constants (string constants, const-modified global constants)

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

//全局变量
int g_a = 10;
int g_b = 10;

//const修饰的全局变量:全局常量
const int c_g_a = 10;
const int c_g_b = 10;

int main()
{
	//全局区

	//全局变量、静态变量、常量


	//创建一个普通的局部变量
	int a = 10;
	int b = 10;
	cout << "局部变量a的地址:" <<(int) &a << endl;//不在全局区
	cout << "局部变量b的地址:" << (int)&b << endl;

	cout << "全局变量g_a的地址:" << (int)&g_a << endl;//与局部变量不在一个区域(全局区)
	cout << "全局变量g_b的地址:" << (int)&g_b << endl;

	//静态变量,在普通变量前加static属于静态变量
	static int s_a = 10;
	static int s_b = 10;
	cout << "静态变量s_a的地址:" << (int)&s_a << endl;//与全局变量在一个区段
	cout << "静态变量s_b的地址:" << (int)&s_b << endl;

	//常量——字符串常量与const修饰的变量
	//字符串常量
	cout << "字符串常量地址:" << (int)&"hello world" << endl;//与全局在同一区段的不同区域
	
	//const修饰的变量
	//const修饰的全局变量,const修饰的局部变量
	cout << "const修饰的全局变量的地址:" << (int) & c_g_a << endl;
	cout << "const修饰的全局变量的地址:" << (int)&c_g_b << endl;
	//const修饰的局部变量
	const int c_l_a = 10;
	const int c_l_b = 10;
	cout << "const修饰的局部变量的地址:" << (int)&c_l_a << endl;//也不在全局区
	cout << "const修饰的局部变量的地址:" << (int)&c_l_b << endl;//带有局部的不在全局区
	system("pause");
	return 0;
}

Summarize:

  • C++ is divided into global area and code area before the program runs
  • The code area is characterized by read-only and shared
  • Store global variables, static variables, and constants in the global area
  • Store const-modified global variables and string constants in the constant area

1.2 After the program runs 

Stack area :

         Automatically allocated and released by the compiler, storing function parameter values, local variables, etc.

        Note: Do not repent the address of the local variable, the data created in the stack area will be automatically released by the compiler

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

//栈区数据的注意事项——不要返回局部变量的地址
//栈区的数据由编译器管理开辟和释放

int* func(int b)//形参数据也会放在栈区
{
	b = 100;
	int a = 10;//局部变量 存放在栈区,栈区的数据在函数执行完后自动释放
	return &a;//返回局部变量的地址
}
int main()
{
	//接收func函数的返回值
	int* p = func(1);
	cout << *p << endl;//第一次能打印正确数字是因为编译器做了保留
	cout << *p << endl;//第二次这个数据就不再保留了(vs2022的X86会显示异常,X64依旧正确)
	system("pause");
	return 0;
}

Heap area :

        It is allocated and released by the programmer. If the programmer does not release it, it will be taken back by the operating system at the end of the program

        In C++, new is mainly used to open up memory in the heap area

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

int* func()//指针类型
{
	//利用new关键字,可以将数据开辟到堆区
	//指针本质也是局部变量,也是放在栈上,指针保存的数据放在堆区
	int* p=new int(10);//new返回的是地址,用指针p来接收
	return p;//返回p地址
}

int main()
{
	//堆区开辟数据
	int* p = func();//指针p接收func返回的地址
	
	cout << *p << endl;//*p解引用出p地址的值,该值在堆区
	system("pause");
	return 0;
}

1.3 new operator

Use the new operator  in C++ to open up data in the heap area

The data developed in the heap area is manually developed by the programmer and released manually. The release uses the operator delete

Syntax: new data type 

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

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

//new的基本语法
int* func()
{
	//在堆区创建衣蛾整型数据
	//new返回的是该数据类型的指针
	int* p=new int(10);
	return p;
}
void test01()
{
	int* p = func();
	cout << *p << endl;
	cout << *p << endl;
	//堆区的数据由程序员开辟释放
	//如果想释放,运用delete
	delete p;//释放该内存
	//cout << *p << endl;//内存已被释放,再次访问就会报错

}
//在堆区利用new开辟数组
void test02()
{
	//创建10个整型数据的数组在堆区
	int*arr =new int[10];//用中括号创建数组,数组有十个元素,返回的依旧是地址
	for (int i = 0; i < 10; i++)
	{
		arr[i] = i + 100;//赋值与栈区一样
	}
	delete []arr;//释放堆区数组,需要加[]
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

2. Quote

2.1 Basic use of references 

Role: alias the variable

Syntax: data type & alias = original name 

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


int main()
{
	//引用的基本语法
	//数据类型 &别名=原名
	int a = 10;
	int &b = a;
	b = 20;
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;

	system("pause");
	return 0;
}

2.2 Notes on citations

  • reference must be initialized
  • After the reference is initialized, it cannot be changed

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


int main()
{
	int a = 10;
	
	//引用必须初始化
	//int& b;//错误的,必须要初始化
	int& b = a;

	//引用在初始化后,就不可以发生改变
	int c = 20;
	//int& b = c;//错误的,不可以发生改变
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
	cout << "c=" << c << endl;
	system("pause");
	return 0;
}

2.3 References as function parameters

Function: When a function passes parameters, you can use the reference technique to let the formal parameters modify the actual parameters

A little bit: it can simplify the pointer to modify the actual parameter

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

//交换函数

//值传递
void myswap01(int a, int b)
{
	int temp = a;
	a = b;
	b = temp;
	cout << "a=" << a << "\tb=" << b << endl;
}
//地址传递
void myswap02(int* a, int* b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
	cout << "a=" << *a << "\tb=" << *b << endl;
}
//引用传递
void  myswap03(int&a,int&b)//别名与原名一样
{
	int temp = a;
	a = b;
	b = temp;
	cout << "a=" << a << "\tb=" << b << endl;
}
int main()
{
	int a = 10 ;
	int b = 20;
	//myswap01(a, b);//值传递,形参不会修饰实参
	cout << "a=" << a << "\tb=" << b << endl;

	//myswap02(&a, &b);//地址传递,形参会修饰实参
	cout << "a=" << a << "\tb=" << b << endl;

	myswap03(a, b);//引用传递,形参会修饰实参
	cout << "a=" << a << "\tb=" << b << endl;
	system("pause");
	return 0;
}

2.4 Reference as function return value

Role: references can exist as return values ​​of functions

NOTE: Do not return local variable references 

Usage: function call as lvalue 

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


//引用做函数的返回值
//1.不要返回局部变量的引用
int& test01()
{
	int a = 10;//局部变量,存放在栈区
	return a;
}
//2.函数的调用可以作为左值
int& test02()//返回的是a的引用,即a变量
{
	static int a = 10;//静态变量,全局区
	return a;
}
int main()
{
	int &ref = test01();
	cout << "ref=" << ref << endl;//正确,因为编译器做了保留(x86与x64不一样)
	cout << "ref=" << ref << endl;//错误,因为内存已经释放

	int& ref2 = test02();//ref2是a的别名
	cout << "ref2=" << ref2 << endl;//正确的,10
	cout << "ref2=" << ref2 << endl;//正确的,10

	//如果函数的返回值是引用,这个函数调用可以作为左值
	test02() = 1000;//变量的返回值=1000,即a=1000;的操作
	cout << "ref2=" << ref2 << endl;//1000
	cout << "ref2=" << ref2 << endl;//1000


	system("pause");
	return 0;
}

 2.5 The nature of citations

 Essence: The essence of the reference is implemented inside C++ as a pointer constant (the point cannot be changed, but the value can be changed)

 C++ recommends using reference counting, because the syntax is convenient, and the essence of a reference is a pointer constant, but all pointer operations are done for us by the compiler

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

void func(int& ref)
{
	ref = 100;
}
int main()
{
	int a = 10;

	//自动转换为 int* const ref = &a;指针常量是指针指向不可改,也说明为什么引用不可改
	int& ref = a;//内部发现ref是引用,自动帮我们转换为*ref=20;
	ref = 20;

	cout << "a=" << a << endl;
	cout << "ref=" << ref << endl;

	func(a);
	cout << "a=" << a << endl;
	cout << "ref=" << ref << endl;
	system("pause");
	return 0;
}

2.6 Constant reference

Role: constant references are mainly used to modify formal parameters to prevent misuse

In the function parameter list, you can add const to modify the parameter to prevent the parameter from changing the actual parameter

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

//打印数据函数
void showvalue(const int& val)
{
	//val = 1000;
	cout << "val=" << val << endl;
}
int main()
{
	//常量引用
	//使用场景:用来修饰形参,防止误操作

	int a = 10;
	//加上const之后,编译器将代码修改为 int temp = 10;const int& ref = temp;
	//const int& ref = 10;//引用必须引一块合法的内存空间
	//ref = 20;//加入const变为只读,不可修改

	int b = 100;
	showvalue(b);
	cout << "b=" << b << endl;
	system("pause");
	return 0;
}

3. Function improvement

3.1 Function default parameters 

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

Syntax: return-type function-name(parameters = default){} 

  • If the data is passed in, use the passed in, if not, use the default value 
  • If a position already has a default parameter, then from this position onwards, from left to right, there must be a default value 
  •  如果函数的声明有了默认参数,那么这个函数的实现就不能有默认参数,声明和实现只能有一个有默认参数
#include<iostream>
using namespace std;

//函数的默认参数

//如果传入了数据,就用传入的,如果没有传入,就用默认值
//语法:返回值类型 函数名(形参=默认值){  }
int func(int a, int b=10, int c=10)
{
	return a + b + c;
}


//注意事项
//1.如果某个位置已经有了默认参数,那么从这个位置往后,从左往右都必须有默认值
	//int func2(int a, int b = 5, int c),错误的,b赋值了,c也要赋值

//2.如果函数的声明有了默认参数,那么这个函数的实现就不能有默认参数
//声明和实现只能有一个有默认参数
int func2(int a, int b);//函数声明

int func2(int a=20, int b=20)
{
	return a + b  ;
}
int main()
{
	func(1);
	cout << func2() << endl;
	system("pause");
	return 0;
}

3.2函数的占位参数

 C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置

语法:返回值类型  函数名   (数据类型){} 

//占位参数

void func(int)//仅仅写一个数据类型
{
	printf("this is func");
}

//占位参数也可以有默认值
void func2(int = 10)
{

}
int main()
{
	func(10);
	system("pause");
	return 0;
}

3.3函数重载

 3.3.1函数重载概述

作用:函数名可以相同,提高复用性 

函数重载满足条件:

  • 同一个作用域下
  • 函数名相同
  • 函数参数类型不同  或者  个数不同  或者  顺序不同 

:函数的返回值不可以作为函数重载的条件 

                比如将 void函数换做int类型函数,函数名不变,也无法函数重载

#include<iostream>
using namespace std;

void func()
{
	printf("调用");
}

void func(int a)
{
	printf("调用!");
}

void func(double a)//类型不同
{
	printf("类型不同");
}

void func(int a,double b)//个数不同
{
	printf("个数不同");
}

void func(double a, int b)//顺序不同
{
	printf("顺序不同");
}
int main()
{
	func(1,1.1);
	system("pause");
	return 0;
}

 3.3.2函数重载的注意事项

  • 引用作为重载条件
  • 函数重载碰到函数默认参数 
#include<iostream>
using namespace std;

//函数重载的注意事项
//1.引用作为重载的条件
void func(int &a)
{
	cout << "func(int &a)调用" << endl;
}

void func(const int& a)//const int &a = 10;合理
{
	cout << "func(const int &a)调用" << endl;
}

//2.函数重载碰到函数默认参数 
void func2(int a,int b=10)
{
	cout << "func2(int a,int b)的调用" << endl;
}

void func2(int a)
{
	cout << "func2(int a=10)的调用" << endl;
}
int main()
{
	int a = 10;
	func(a);//a是变量,可读可写

	func(10);//10是常量,调用第二个

	func2(20);//函数重载碰到函数默认参数 ,报错,有二义性
	system("pause");
	return 0;
}

4.类和对象

C++面向对象的三大特性为:封装、继承、多态

C++认为万事万物都皆为对象 ,对象上有其属性和行为

例如:

        人可以作为对象,属性有姓名、年龄、身高、体重...,行为有走、跑、跳、吃饭、唱歌...

        车也可以作为对象,属性有轮胎、方向盘、车灯..,行为有载人、放音乐、放空调...

        具有相同性质的对象,我们可以抽象称为,人属于人类,车属于车雷类

4.1封装 

4.1.1封装的意义 

封装是C++面向对象三大特性之一

封装的意义:

  • 将属性和行为作为一个 整体,表现生活中的事物
  • 将属性和行为加以权限控制

封装意义一

        在设计类的时候,属性和行为写在一起,表现事物 

语法: class 类名{   访问权限:   属性  /  行为  };

  •     类中的属性和行为统一成为  成员
  •     属性  分为成员属性 成员变量
  •     行为   分为成员函数  成员函数

 给属性赋值也可以在类中,参考该节例题

#include<iostream>
using namespace std;

const double PI = 3.14;
//设计一个圆类,求圆的周长
//圆求周长的公式:2*PI*R
//class代表类,后面紧跟着就是类名
class  circle
{
	//访问权限
		//公共权限
public:
	//属性
		//半径
	int m_R;
	//行为,通常用函数
		//获取圆的周长
	double calculate()
	{
		return 2 * PI * m_R;
	}
};
int main()
{
	//通过圆类,创建具体的圆(对象)
    //实例化:通过一个类,创建一个对象的过程
	circle c1;
	//给圆对象 的属性进行赋值,通过 .访问属性
	c1.m_R = 10;
	cout << "圆的周长是:" << c1.calculate() << endl;

	system("pause");
	return 0;
}

例:设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号

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

//设计学生类
class student
{
	//访问权限
public://公共权限

	//类中的属性和行为统一成为  成员
	// 属性  分为成员属性 成员变量
	// 行为   分为成员函数  成员函数
	//属性
	string name;//姓名
	int number;//学号
	//行为
	void show()//显示姓名学号
	{
		cout << name <<":"<< number << endl;
	}

	//给属性赋值**
	void setname(string name1,int number1)
	{
		name = name1;
		number = number1;
	}
};
int main()
{
	//实例化:创建具体学生
	student s1;
	//给s1进行属性赋值
	s1.name = "张三";
	s1.number = 2022;
	//显示学生信息
	s1.show();
	
	//实例化s2
	student s2;
	s2.setname("里斯",2023);
	s2.show();
	system("pause");
	return 0;
}

封装意义二

类在设计师,可以把属性和行为放在不同的权限下,加以控制

访问权限有三种:

  • 1.public        公共权限       

        成员在类内可以访问,类外也可以访问

  • 2.protected        保护权限        

        类内可以访问,类外不可以访问(继承中:父类权限子类可以访问)

  • 3.private        私有权限        

         类内可以访问,类外不可以访问(继承中:父类权限子类不可访问)

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


class person
{	
public://公共权限
	string name;//姓名

protected://保护权限
	int age;//年龄

private://私有权限
	int password;//密码
public:
	void func()//类内访问,均无问题
	{
		name = "张三";
		age = 18;
		password = 123456;
	}
};
int main()
{
	//实例化具体对象
	person p1;
	p1.name = "里斯";//公共,可以访问
	p1.age = 20;//保护,无法访问
	p1.password = 654321;//私有,不可访问
	system("pause");
	return 0;
}

 4.1.2struct和class的区别

在c++中struct和class唯一的区别就在于 默认的访问权限不同 

区别:

  • struct 默认权限为公共
  • class 默认权限为私有 
#include<iostream>
using namespace std;
#include"string"

struct c1
{
	int A;//默认权限是公共
};

class c2
{
	int B;//默认权限是私有
};
int main()
{
	c1 s1;
	s1.A = 100;//正确的

	c2 s2;
	s2.B = 50;//错误的,私有不可访问
	system("pause");
	return 0;
}

 4.1.3成员属性设置为私有

 优点1:将所有成员属性设置为私有,可以自己控制读写权限

优点2:对于写权限,我们可以检测数据的有效性

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

class person
{
	string name;//姓名
	int age;//年龄
	string wife;//妻子
public://对外接口
	//姓名可读可写
		//写姓名(设置姓名)
	void setname(string name1)
	{
		name = name1;
	}
		//读姓名(获取姓名)
	string getname()
	{
		return name;
	}
	
	//年龄读写
		//写
	void setage(int age1)
	{
		if (age1 < 0 || age1>150)//对于写权限,我们可以检测数据的有效性
		{
			age = 0;
			return;
		}
		age = age1;
	}
		//读
	int getage()
	{
		return age;
	}

	//妻子只写
	void setwife(string wife1)
	{
		wife = wife1;
	}
};
int main()
{
	person p1;
	p1.setname("张三");//正确的,可写
	cout << p1.getname() << endl;//正确的,可读

	p1.setage(12);//正确,可写
	cout << p1.getage() << endl;//正确的,可读

	p1.setwife("里斯");
	//cout <<p1.setwife() << endl;//错误的,不可读

	system("pause");
	return 0;
}

练习案例1:设计立方体类

 设计立方体类(cube)

求出立方体的面积和体积

分别用全局函数和成员函数判断两个立方体是否相等

 成员函数判断时候只需要传入一个

全局函数判断时候需要传入两个

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

//设计立方体类
class cube
{
public:
	double m_L;//长
	double m_H;//高
	double m_W;//宽

	double mianji()//求出立方体面积
	{
		return (m_L * m_H+ m_L * m_W+ m_W * m_H) * 2;
	}
	double tiji()//求出立方体体积
	{
		return m_L * m_H * m_W;
	}
	double fuzhi(double L, double H, double W)//赋值
	{
		m_L = L;
		m_H = H;
		m_W = W;
		return (m_L, m_H, m_W);
	}
	void panduan(cube& c2)//成员函数判断两个立方体是否相等
	{
		if (m_L== c2.m_L&&m_H==c2.m_H&&m_W==c2.m_W)
		{
			cout << "两个立方体相等" << endl;
		}
		else
		{
			cout << "两个立方体不相等" << endl;
		}
	}

};

//利用全局函数判断两个立方体是否相等
void panduan(cube &c1,cube &c2)//用引用传递,不会拷贝数据
{
	if (c1.fuzhi(1, 2, 3) == c2.fuzhi(1, 2, 3))
	{
		cout << "两个立方体相等" << endl;
	}
	else
	{
		cout << "两个立方体不相等" << endl;
	}
}
int main()
{
	cube c1;
	cube c2;
	c1.fuzhi(1, 2, 3);
	cout << "c1面积" << c1.mianji() << "c1体积" << c1.tiji() << endl;
	c2.fuzhi(1, 2, 3);
	cout << "c2面积" << c2.mianji() << "c2体积" << c2.tiji() << endl;

	panduan(c1, c2);//传入两个做对比

	c1.panduan(c2);//c1的内属性与c2做对比
	system("pause");
	return 0;
}

 练习案例2:点和圆的关系

 设计一个圆形类(Circle),和一个点类(point),计算点和圆的关系

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

class  circle//圆类
{
public:
	double m_R;//半径
	double O[2];//圆心
	double setR(double R)//读写圆的半径
	{
		m_R = R;
		return m_R;
	}
	double setO(double x, double y)//读写圆心
	{
		O[0] = x;
		O[1] = y;
		return(O[0], O[1]);
	}
};
class point//点类
{
public:
	double m_P[2];//点的位置
	double setP(double x, double y)//读写点的位置
	{
		m_P[0] = x;
		m_P[1] = y;
		return(m_P[0], m_P[1]);
	}
	void panduan(circle& c)//成员函数判断点处于圆的位置
	{
		if (m_P[0] * m_P[0] + m_P[1] * m_P[1] > c.m_R * c.m_R)//点到圆的位置大于半径
		{
			cout << "点在圆外" << endl;
		}
		else if (m_P[0] * m_P[0] + m_P[1] * m_P[1] == c.m_R * c.m_R)//点到圆的位置等于半径
		{
			cout << "点在圆上" << endl;
		}
		else//点到圆的位置小于半径
		{
			cout << "点在圆内" << endl;
		}
	}
};
int main()
{
	circle c;//实例化圆
	point p;//实例化点
	c.m_R = 10;//半径
	c.setO(0, 0);//圆心
	p.setP(10, 0);//点的坐标
	p.panduan(c);//比较
	system("pause");
	return 0;
}

 类的拆分

 对于点和圆的关系的代码中,类出现了两个,在代码很大的时候,可以进行类的拆分

1.新建一个头文件,命名为类名.h,如circle.h

 2.新建一个源文件,与头文件同名,如circle.cpp

3. Similarly, another class (here is the point class: point) is the same as the first and second steps

 

 4. The original source file can be written in the header file

 4.2 Object initialization and cleanup

  •  The electronic products we buy in life basically have factory settings, and some of our own information and data will be deleted to ensure safety when we don’t use them one day
  • Object-oriented source and life in c++, each object will also have initial settings and settings for cleaning data before the object is destroyed

 4.2.1 Constructor and destructor

 Object initialization and cleanup are also two very important security issues 

        An object or variable has no initial state, and the consequences of using it are unknown

        Similarly, after using an object or variable, if it is not cleaned up in time, it will also cause certain security problems.

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 cleanup.

The initialization and cleanup of objects is what the compiler forces us to do, so if we don't provide construction and destruction, the compiler will provide

The constructor and destructor provided by the compiler are empty implementations

  • Constructor: The main function is to assign values ​​to the member properties 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 automatically calls  before the object is destroyed to perform some cleaning work

 Constructor syntax: class name () {}

  • 1. Constructor, no return value and no void
  • 2. The function name is the same as the class name
  • 3. The constructor can have parameters, so overloading can occur
  • 4. The program will automatically call the constructor when calling the object, no need to call it manually, and it will only be called once

 Destructor syntax: ~class name(){}

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

class person
{
public:
	person()
	{
		cout << "person构造函数的调用" << endl;
	}

	~person()
	{
		cout << "person析构函数的调用" << endl;
	}
};

void test01()
{
	person p;//栈上的数据,test01执行完毕后,释放这个对象,所以会调用析构
}
int main()
{
	test01();
	//person p;//main函数执行结束才会释放,所以界面不显示析构
	system("pause");
	return 0;
}

4.2.2 Classification and call of constructor

Two classification methods:

        Divided by parameters: construction with parameters and construction without parameters person()/person(int a)

        Divided by type: ordinary construction and copy construction person(const person &p)

Three calling methods:

        括号法            person p2(10);//有参构造函数
                               person p3(p2);//拷贝构造函数

        显示法            person p5 = person(10);//有参构造
                               person p6=person(p5); // 拷贝构造

        隐式转换法     person p7 = 10;//相当于 person p7 = person(10);
                               person p8 = p7;//拷贝构造

#include<iostream>
using namespace std;

class person
{
public:
	person()//构造函数
	{
		cout << "person的无参构造函数调用" << endl;
	}
	person(int a)//有参构造函数
	{
		age = a;
		cout << "person的有参构造函数调用" << endl;
	}
	person(const person &p)//拷贝构造函数
	{
		//将传入的人身上的所有属性,拷贝到当前对象身上
		age = p.age;
		cout << "person的拷贝函数调用" << endl;
	}
	~person()//析构函数
	{
		cout << "person的析构函数调用" << endl;
	}
	int age;
};
//调用
void test01()
{
	//括号法
	person p1;// 默认构造函数的调用
	person p2(10);//有参构造函数
	person p3(p2);//拷贝构造函数
	//注意事项:
	// 调用默认构造函数的时候,不要加()
	//因为下面这行代码,编译器会认为是函数声明,不会认为在创建对象
	//person p1();
	
	//显示法
	person p4;
	person p5 = person(10);//有参构造
	person p6=person(p5); // 拷贝构造

	person(10);//匿名对象、特点:当前行执行结束后,系统会立即回收匿名对象
	//注意事项2
	// 不要利用拷贝构造函数来初始化匿名对象,编译器会认为person (p5)===person 5;对象声明
	person(p5);

	//隐式转换法
	person p7 = 10;//相当于 person p7 = person(10);
	person p8 = p7;//拷贝构造

}

int main()
{
	test01();
	system("pause");
	return 0;
}

4.2.3拷贝构造函数调用时机

C++中拷贝构造函数调用时机通常有三种情况

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 以值方式返回局部对象 
#include<iostream>
using namespace std;


class person
{
public:
	person()
	{
		cout << "person默认构造函数调用" << endl;
	}
	person(int age) 
	{
		m_age = age;
		cout << "person有参构造函数调用" << endl;
	}
	person(const person& p)
	{
		m_age = p.m_age;
		cout << "person拷贝构造函数调用" << endl;
	}
	~person()
	{
		cout << "person析构函数调用" << endl;
	}
	int m_age;
};
//使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{
	person p1(20);
	person p2(p1);
	cout << "p2年龄" << p2.m_age << endl;
}
//值传递的方式给函数参数传值
void dowork(person p)
{

}
void test02()
{
	person p;
	dowork(p);

}
//以值方式返回局部对象 
person dowork2()
{
	person p5;
	return p5;
}
void test03()
{
	person p6 = dowork2();
}
int main()
{
	test01();
	test02();
	test03();
	system("pause");
	return 0;
}

4.2.4构造函数的调用规则

默认情况下,C++编译器至少给一个类添加3个函数

1.默认构造函数(无参,函数体为空)

2.默认析构函数(无参,函数体为空)

3.默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下:

  • 如果用户定义有参构造函数,C++不在提供默认无参构造,但是会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,C++不会再提供其他构造函数

 4.2.5深拷贝与浅拷贝

深浅拷贝是面试经典问题,也是常见的一个坑

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间。进行拷贝操作

         浅拷贝带来的问题:堆区内存重复释放,可以利用深拷贝进行解决

如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

#include<iostream>
using namespace std;


class person
{
public:
	person()
	{
		cout << "person默认构造函数调用" << endl;
	}
	person(int age,int height)
	{
		m_age = age;
		m_height=new int(height);
		cout << "person有参构造函数调用" << endl;
	}
	//自己实现拷贝构造函数,解决浅拷贝带来的问题
	person(const person& p)
	{
		cout << "person拷贝构造函数调用" << endl;
		m_age = p.m_age;
		//m_height=p.m_height//编译器默认实现的就是这行代码
		//深拷贝操作

		m_height=new int(*p.m_height);
	}
	~person()
	{
		//析构代码,将堆区开辟数据做释放操作
		if (m_height != NULL)
		{
			delete m_height;
			m_height = NULL;
		}
		cout << "person析构函数调用" << endl;
	}
	int m_age;
	int* m_height;
};

void test01()
{
	person p1(18,160);
	cout << "p1年龄" << p1.m_age << "身高"<<*p1.m_height<<endl;
	person p2(p1);
	cout << "p2年龄" << p2.m_age << "身高" << *p2.m_height << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.2.6初始化列表

作用:C++提供了初始化列表语法,用来初始化属性

语法:构造函数():属性1(值1),属性2(值2)...{} 

#include<iostream>
using namespace std;

class person
{
public:
	//传统初始化操作
	//person(int a, int b, int c)
	//{
	//	m_a = a;
	//	m_b = b;
	//	m_c = c;
	//}
	//初始化列表
	person(int a, int b, int c) :m_a(a), m_b(b), m_c(c)
	{

	}
	int m_a;
	int m_b;
	int m_c;
};
void test01()
{
	person p(10, 20, 30);
	cout <<"m_a="<< p.m_a << endl;
	cout << "m_b=" << p.m_b << endl;
	cout << "m_c=" << p.m_c << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.2.7 Class objects as class members

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

For example:

class A ()

class B

{

        A  a;

Class B has object A as a member, and A is an object member 

Then when the B object is created. Who comes first in the order of construction and destruction of A and B? 

  •          When other class objects are members of this class, the class object is constructed first, and then itself is constructed. The order of destruction is opposite to that of construction.
#include<iostream>
using namespace std;
#include"string"

class phone
{
public:
	phone(string pname)
	{
		p_name = pname;
		cout << "phone的构造函数调用" << endl;
	}
	~phone()
	{
		cout << "phone的析构函数调用" << endl;
	}
	string p_name;//品牌名
};
class person
{
public:
	person(string name, string pname):m_name(name),m_phone(pname)
	{
		cout << "person的构造函数调用" << endl;
	}
	~person()
	{
		cout << "person的析构函数调用" << endl;
	}
	string m_name;//姓名

	phone m_phone;//手机
};
//当其他类对象作为本类成员,构造时候先构造类对象,再构造自身,析构的顺序?
void test01()
{
	person p("张三", "苹果");
	cout << p.m_name <<p.m_phone.p_name << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

 4.2.8 Static members

Static member is to add keyword static before member variable and member function, called static member

Static members are divided into:

  • static member variable
  1.         All objects share the same data
  2.         Allocate memory during compilation
  3.         In-class declaration, out-of-class initialization
  • static member function
  1.         All objects share the same function
  2.         Static member functions can only access static member variables
#include<iostream>
using namespace std;

class person
{
public:
	static int m_a;//静态成员变量,类内声明

	//静态成员变量也是有访问权限的
private:
	static int m_b;
};
int person::m_a = 100;//类外初始化,避免全局变量,需要声明person作用域下的变量
int person::m_b = 200;
void test01()
{

	person p;
	cout << p.m_a << endl;//100
	person p2;
	p2.m_a = 200;//p2对象的值改为200
	cout << p.m_a << endl;//p的值也为200,所有对象共享同一份数据
}
void test02()
{
	//因此静态成员变量有两种访问方式
	//1.通过对象进行访问
	person p3;
	cout << p3.m_a << endl;
	//2.通过类名进行访问
	cout << person::m_a << endl;

	//cout << person::m_b << endl;//报错,不可访问,私有作用域
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

static member function 

#include<iostream>
using namespace std;

//所有对象共享同一个函数
//静态成员函数只能访问静态成员变量
class person
{
public:
	static void func()//静态成员函数
	{
		m_b = 100;
		//m_a = 50;//报错,非静态成员变量,静态成员函数无法访问,无法区分是那个对象的属性
		cout << "static void func 的调用" << endl;
	}
	int m_a;
	static int m_b;
	//静态成员函数也是有访问权限的
private:
	static void func1()
	{
		cout << "static void func1的调用" << endl;
	}
};
int person::m_b=0;
//有两种访问方式
void test01()
{
	//1.通过对象调用
	person p;
	p.func();
	//2.通过类名调用
	person::func();

	//person::func1();//报错,无法访问
}
int main()
{
	test01();
	system("pause");
	return 0;
}

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 the class 

tip: The byte size of an empty class object is 1,

#include<iostream>
using namespace std;


class person//空类
{

};
class person1
{
public:
	int m_a;//非静态成员变量  属于类的对象上的
};
class person2
{
	static int m_b;//静态成员变量,不属于类的对象上的
	void func(){}//添加非静态成员函数,不属于类的对象上
	static void func1(){}//添加静态成员函数,不属于类的对象上
};
int person2::m_b = 5;

void test01()
{
	person p;
	//空对象占用的内存空间
	cout << "size of p = " << sizeof(p) << endl;//结果是1,C++编译器会给每个空对象也分配一个字节的空间是为了区分空对象占内存的位置
}
void test02()
{
	person1 p1;
	cout << "size of p1 = " << sizeof(p1) << endl;//结果是4个字节
}
void test03()
{
	person2 p2;
	cout << "size of p2 = " << sizeof(p2) << endl;//仍然是1,静态成员变量不属于对象上
	//添加非静态成员函数后,结果仍然是1,因为成员变量与成员函数是分开存储的
}
int main()
{
	test01();
	test02();
	test03();
	system("pause");
	return 0;
}

4.3.2 The concept of this pointer

Through 4.3.1 we know that member variables and member functions are stored separately in C++

Each non-static member function will only generate a function instance, that is to say, multiple objects of the same type will share a piece of code, so the question is: how does this piece of code distinguish which object calls itself?

C++ does this by providing special object pointers. this pointer, to solve the above problem, this pointer points to the object to which the called member function belongs 

The this pointer is a pointer implicit in every non-static member function

The this pointer does not need to be defined, it can be used directly

The purpose of this pointer:

  • When the formal parameter and the member variable have the same name, the this pointer can be used to distinguish them
  • To return the object itself in a non-static member function of a class, use return *this

#include<iostream>
using namespace std;

class person
{
public:
	person(int age) 
	{
		//this指向的是被调用的成员函数所属的对象
		this->age=age;
	}
	person& personaddage(person &p)//用引用接受指针,不用引用的话就是返回的值,会调用拷贝构造函数,创建一个新的对象,无法链式
	{
		this->age += p.age;//自身的年龄=别人的年龄+自身的
		return *this;
	}
	int age;
};

//解决名称冲突
void test01()
{
	person p1(18);
	cout << "p1的年龄" << p1.age << endl;
}
//返回对象本身用 *this
void test02()
{
	person p2(10);
	person p3(10);
	p3.personaddage(p2).personaddage(p2);//链式编程思想
	cout << "p3的年龄是:" << p3.age << endl;
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

4.3.3 Null pointer access to member functions

Null pointers in C++ can also call member functions, but also pay attention to whether the this pointer is used

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 chass" << endl;
	}
	void showpersonage()
	{
		if (this == NULL)//加个判断保持健壮性
		{
			return;
		}
		cout << "age=" << m_age << endl;
	}
	int m_age;
};
void test01()
{
	person* p = NULL;
	p->showclassname();
	p->showpersonage();//m_age默认是this->m_age;因为没有确定的对象,是空指针,访问不到里面的属性
}
int main()
{
	test01();
	system("pause");
	return 0;
}

 4.3.4cosnt modified member function 

 Constant function :

  • After adding const to the member function, we call this function a constant function
  • Member attributes cannot be modified in constant functions
  • After adding mutable to the member attribute declaration, it can still be modified in the constant function

Constant object :

  • Add const before declaring an object to call it a constant object
  • Constant objects can only call constant functions

#include <iostream>
using namespace std;

//常函数
class person
{
public:
	//this指针的本质是 指针常量person * const this 指针的指向是不可以修改的
	void showperson()const//该处的const相当于 const person* const this;值也不可修改
	{
		//this = NULL;//this指针的指向是不可修改的
		//this->m_a = 100;//值是可以修改的
		this->m_b = 100; //依然可以修改值
	}
	void func()
	{

	}
	int m_a;
	mutable int m_b;//特殊变量,即使在常函数中也可以修改这个值
};
void test01()
{
	person p;
	p.showperson();
}
//常对象
void test02()
{
	const person p;//在对象前加const,成为常对象
	//p.m_a = 100;//不可修改
	p.m_b = 100;//可以修改

	//常对象只能调用常函数
	p.showperson();//可以调用
	//p.func();//不能调用,因为普通成员函数可以修改属性
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

4.4 Tomomoto

In life, your home has a living room (public) and a bedroom (private)

All guests in the living room can enter, but your bedroom is private, which means only you can enter

But, you can also allow your good girlfriends and gay friends to enter

In the program, if there are some private properties, and some special functions or classes outside the class can also be accessed, you need to use friends

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

The keyword of the friend is friend

Three Realizations of Friends

  • Global functions as friends
  • class as friend
  • member function as friend 

 1. Global functions as friends

        Just unload the declaration of the global function from the first line in the class, and then add the friend keyword friend

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

class building
{
	//goodgay全局函数是building的好朋友,可以访问building的私有成员
	friend void goodgay(building* building);
public:
	building():m_settingroom("客厅"),m_bedroom("卧室")
	{
	}
public:
	string m_settingroom;//客厅

private:
	string m_bedroom;//卧室
};
void goodgay(building *building)
{
	cout << "好基友的全局函数 正在访问:" << building->m_settingroom << endl;
	cout << "好基友的全局函数 正在访问:" << building->m_bedroom << endl;
}
void test01()
{
	building building;
	goodgay(&building);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

2. Classes as friends

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

class building;//声明有个building
class goodgay
{
public:
	goodgay();
	void visit();//参观函数,访问building中的属性
	building* building1;
};
class building
{
	//goodgay类是building类的好朋友,可以访问私有成员
	friend class goodgay;
public:
	building();
public:
	string m_sittingroom;
private:
	string m_bedroom;
};
//类外写成员函数
building::building():m_sittingroom("客厅"),m_bedroom("卧室")
{
}
goodgay::goodgay():building1(new building)
{
}
void goodgay::visit()
{
	cout << "好基友类正在访问:" << building1->m_sittingroom << endl;
	cout << "好基友类正在访问:" << building1->m_bedroom << endl;
}
void test01()
{
	goodgay hh;
	hh.visit();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3. Member functions as friends

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

class building;//声明有个building
class goodgay
{
public:
	goodgay();
	void visit();//参观函数,访问building中的私有成员
	void visit2();//参观函数,不能访问building中的私有成员
	building* building1;
};
class building
{
	//goodgay类下的visit函数可以访问私有成员
	friend void goodgay::visit();
public:
	building();
public:
	string m_sittingroom;
private:
	string m_bedroom;
};
//类外写成员函数
building::building() :m_sittingroom("客厅"), m_bedroom("卧室")
{
}
goodgay::goodgay() :building1(new building)
{
}
void goodgay::visit()
{
	cout << "visit正在访问:" << building1->m_sittingroom << endl;
	cout << "visit正在访问:" << building1->m_bedroom << endl;
}
void goodgay::visit2()
{
	cout << "visit2正在访问:" << building1->m_sittingroom << endl;
	cout << "visit2正在访问:" << building1->m_bedroom << endl;
}
void test01()
{
	goodgay hh;
	hh.visit();
	hh.visit2();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.5 Operator overloading

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

4.5.1 Plus Operator Overloading

Function: realize the operation of adding two custom data types 

    1. Member function overload + number
    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;     }




 2. Global function overload + number
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; }




3. Operator overloading, function overloading can also occur
person operator+(person& p1, int num)
{     person temp;     temp.m_A = p1.m_A + num;     temp.m_B = p1.m_B +num;     return temp; }




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

//加号运算符重载
class person
{
public:
	//1.成员函数重载+号
	//person operator+(person& p)
	//{
	//	person temp;
	//	temp.m_A = this->m_A + p.m_A;
	//	temp.m_B = this->m_B + p.m_B;
	//	return temp;
	//}
	int m_A;
	int m_B;
};
//2.全局函数重载+号
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;
}
//函数重载的版本
person operator+(person& p1, int num)
{
	person temp;
	temp.m_A = p1.m_A + num;
	temp.m_B = p1.m_B +num;
	return temp;
}
void test01()
{
	person p1;
	p1.m_A = 10;
	p1.m_B = 10;

	person p2;
	p2.m_A = 10;
	p2.m_B = 10;

	//成员函数重载本质调用
	//person p3 = p1.operator+(p2);

	//全局函数重载的本质调用
	//person p3 = operator+  (p1, p2);

	person p3 = p1 + p2;//两种均可以简化成此种形式

	//运算符重载,也可以发生函数重载
	person p4 = p1 + 100;//person+int

	cout << "p3.m_A=" << p3.m_A << "\np3.m_B=" << p3.m_B << endl;
	cout << "p4.m_A=" << p4.m_A << "\np4.m_B=" << p4.m_B << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}

Summary 1: It is impossible to change the operators of expressions of built-in data types

Summary 2: Don't abuse operator overloading

4.5.2 Left shift operator overloading

Role: can output custom data types

     1. Use member functions to overload the left shift operator
    Usually do not overload the left shift operator with member functions, because cout cannot be realized on the left
    void operator<<(cout){} //p.operator<<(cout)== p<<cout

2. You can only use the global function to overload the left shift operator
        cout belongs to ostream: output stream type
ostream &operator<<(ostream &cout,person p)//Essence: operator<<(cout,p)==cout<<p;
{     cout << "m_A=" << p.m_A << "\nm_B=" << p.m_B;     return cout;// need to return cout type to continue chain programming }


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

class person
{
	friend ostream& operator<<(ostream& cout, person p);
	friend void test01();
	//利用成员函数重载左移运算符
	//通常不用成员函数重载左移运算符,因为无法实现cout在左侧
	//void operator<<(cout){}  //p.operator<<(cout)==p<<cout

	int m_A;
	int m_B;

};
//只能利用全局函数重载左移运算符
//cout属于ostream:输出流类型
ostream &operator<<(ostream &cout,person p)//本质:operator<<(cout,p)==cout<<p;
{
	cout << "m_A=" << p.m_A << "\nm_B=" << p.m_B;
	return cout;//需要返回cout类型,才能继续链式编程
}
void test01()
{
	person p;
	p.m_A = 10;
	p.m_B = 10;
	cout << p << endl;//
}
int main()
{
	test01();
	system("pause");
	return 0;
}

Summary: Overloading the left shift operator with friends can realize the output of custom data types

4.5.3 Increment operator overloading

Role: Realize your own plastic data by overloading the increment operator 

Pre-increment: what is returned is a reference

myinteger& operator++() //The return reference is to always increment a data
    {         m_num = this->m_num++;

        return *this; //this points to itself, *this dereferences
    }

post-increment: returns the value

    myinteger operator++(int) //int is a placeholder parameter, which can be used to distinguish between pre-increment and post-increment, and post-increment returns a value instead of a reference
    {         myinteger temp = *this; 
       //Record the result at that time
        m_num++;         // Post-increment
        return temp;         //Finally, return the recorded result
    } 

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

class myinteger
{
	friend ostream& operator<<(ostream& cout, myinteger myint);
public:
	myinteger():m_num(0){}
	//重载前置++运算符
	myinteger& operator++()//返回引用是为了一直对一个数据进行递增操作
	{
		m_num = this->m_num++;
		return *this;//this指向自身,*this解引用
	}
	//重载后置++运算符
	myinteger operator++(int)//int 占位参数,可以用于区分前置和后置递增,后置递增返回的是值而非引用
	{
		//先 记录当时结果
		myinteger temp = *this;
		//后  递增
		m_num++;
		//最后,将记录的结果返回
		return temp;
	}
private:
	int m_num;
};
ostream& operator<<(ostream& cout, myinteger myint)//重载左移运算符
{
	cout << myint.m_num;
	return cout;
}
void test01()
{
	myinteger myint;
	cout << myint << endl;
	cout << ++(++myint) << endl;

	cout << myint << endl;
}
void test02()
{
	myinteger myint;
	cout<< myint++ << endl;
	cout << myint << endl;
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

 4.5.4 Assignment operator overloading

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

  1. default constructor (no parameters, function body is empty
  2. Default destructor (no arguments, function body is empty)
  3. The default copy constructor, which copies the value of the property
  4. Assignment operator operator=, copy the value of the attribute

 If there are attributes in the class pointing to the heap area, there will also be deep and shallow copy problems when doing assignment operations

#include <iostream>
using namespace std;

class person
{
public:
	person(int age)
	{
		m_age = new int(age);//创建到堆区
	}
	~person()//堆区数据由程序员开辟释放,在析构时释放
	{
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
		}
	}
	//重载  赋值运算符
	person& operator=(person& p)
	{
		//编译器提供浅拷贝m_age=p.m_age;
		//应该先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
		}
		//深拷贝
		m_age = new int(*p.m_age);
		//返回对象本身
		return *this;
	}
	int *m_age;
};
void test01()
{
	person p1(18);
	person p2(20);
	person p3(30);
	p2 = p1;//赋值运算操作
	p3 = p2 = p1;//连等赋值
	cout << "p1的年龄时:" << *p1.m_age<<endl;
	cout << "p2的年龄时:" << *p2.m_age << endl;
	cout << "p3的年龄时:" << *p3.m_age << endl;
}
int main()
{
	test01();
	//int a = 10;
	//int b = 20;
	//int c = 30;
	//c = b = a;
	//cout << "a=" << a << "b=" << b << "c=" << c << endl;//内置赋值允许连等
	system("pause");
	return 0;
}

 4.5.5 Relational operator overloading

Role: Overload relational operators, allowing two custom type objects to perform comparison operations

#include <iostream>
using namespace std;

class person
{
public:
	person(string name,int age):name(name),age(age){}
	//重载关系运算符==
	bool operator==(person &p)
	{
		if (this->name == p.name && this->age == p.age)
		{
			return true;
		}
		return false;
	}
	//重载!=
	bool operator!=(person& p)
	{
		if (this->name != p.name || this->age != p.age)
		{
			return true;
		}
		return false;
	}
	string name;
	int age;
};
void test01()
{
	person p1("张三", 18);
	person p2("张三", 8);
	if (p1 == p2)
	{
		cout << "p1,p2相等" << endl;
	}
	else 
	{
		cout << "p1,p2不相等" << endl;
	}
	if (p1 != p2)
	{
		cout << "p1,p2不相等" << endl;
	}
	else
	{
		cout << "p1,p2相等" << endl;
	}
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.5.6 Function call operator overloading

  • The function call operator() can also be overloaded
  • Because the method used after overloading is very similar to the call of a function, it is called a functor
  • Functor has no fixed way of writing and is very flexible
#include <iostream>
using namespace std;
#include "string"

class myprint//打印输出类
{
public:
	//重载函数调用运算符
	void operator()(string test)
	{
		cout << test << endl;
	}
};
void test01()
{
	myprint myprint;
	myprint("hello world");
}
//仿函数非常灵活,没有固定写法
class myadd//加法类
{
public:
	int operator()(int num1,int num2) 
	{
		return num1 + num2;
	}
};
void test02()
{
	myadd add;
	int a=add(1, 2);
	cout << a << endl;
	//匿名函数对象
	cout << myadd()(100, 100) << endl;
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

4.6 Inheritance

Inheritance is one of the three characteristics of object-oriented

 In addition to the commonality of the upper level, lower-level members also have their own characteristics. At this time, inheritance can be considered to reduce duplication of code

4.6.1 Basic Syntax of Inheritance 

 Benefit: Reduce Duplicate Code

Syntax: class subclass: inheritance method parent class  

A subclass is also called a derived class and a parent class is also called a base class 

Members in subclasses (derived classes), including two parts:

  • One class is inherited from the parent class, and the other class is added by itself
  • Inherited performance commonality, own performance individuality 
#include <iostream>
using namespace std;

//继承实现页面
class basepage//公共页面
{
public:
	void header()
	{
		cout << "首页。公开课。登录。。。" << endl;
	}
	void footer()
	{
		cout << "交流合作。站内地图。。。" << endl;
	}
	void left()
	{
		cout << "java,python,c++。。。" << endl;
	}
};
class python :public basepage//pyhton继承
{
public:
	void content()
	{
		cout << "python学习" << endl;
	}
};
class java :public basepage//java继承
{
public:
	void content()
	{
		cout << "java学习" << endl;
	}
};
class CPP :public basepage//C++继承
{
public:
	void content()
	{
		cout << "C++学习" << endl;
	}
};
void test01()
{
	cout << "这是java的内容" << endl;
	java ja;
	ja.header();
	ja.footer();
	ja.left();
	ja.content();
	cout << "-------------------" << endl;
	cout << "这是python的内容" << endl;
	python py;
	py.header();
	py.footer();
	py.left();
	py.content();
	cout << "-------------------" << endl;
	cout << "这是C++的内容" << endl;
	CPP c;
	c.header();
	c.footer();
	c.left();
	c.content();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.6.2 Inheritance method

Syntax: class subclass: inheritance method parent class  

There are three types of inheritance:

  1. Public inheritance: parent public, child public, parent protected, child protected, parent private, child inaccessible
  2. Protection inheritance: parent public, child protected, parent protected, child protected, parent private, child inaccessible
  3. Private inheritance: parent public, child private, parent protected, child private, parent private, child inaccessible

 4.6.3 Object Models in Inheritance

问题?从父类继承过来的成员,那些属于子类对象中? 

 下面代码可以看到,父类中的所有非静态成员,子类都会继承,私有成员也会被继承,只是无法访问,被编译器隐藏了。

#include <iostream>
using namespace std;

class base
{
public:
	int m_a;//4字节
protected:
	int m_b;//4字节
private:
	int m_c;//4字节

};
class son :public base
{
public:
	int m_d;//4字节
};
void test01()
{
	cout << "size of son=" << sizeof(son) << endl;//16字节
}
int main()
{
	test01();
	system("pause");
	return 0;
}

也可利用开发人员命令提示工具查看对象模型

 打开vs的开发人员命令符

跳转到文件的路径下

输入cl /d1 reportSingleClassLayout 类名 文件名

4.6.4继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数

问题?父类和子类的构造和析构顺序是谁先谁后?

 答:先base构造,在son构造,再son析构,最后base析构

#include <iostream>
using namespace std;

class base
{
public:
	base()
	{
		cout << "base构造" << endl;
	}
	~base()
	{
		cout << "base析构" << endl;
	}
};
class son :public base
{
public:
	son()
	{
		cout << "son构造" << endl;
	}
	~son()
	{
		cout << "son析构"<<endl;
	}
};
void test01()
{
	son s;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.6.5继承同名成员处理方式

 问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?

  • 访问子类同名成员,直接访问即可
  • 访问父类同名成员,需要加作用域

    cout << s.m_a << endl;//son的同名,直接访问
    cout << s.base::m_a << endl;//父类同名,加作用域

    s.func();//子类的同名函数,直接访问
    s.base::func();//父类的同名函数,加作用域

    //s.func(1);//子类出现和父类同名的成员函数,那子类的成员会隐藏掉父类的所有同名的函数
    s.base::func(1);//加作用域,才可访问 

总结:

  • 子类对象可以直接访问到子类同名成员
  • 子类对象加作用域可以访问到父类同名成员
  • 当子类和父类有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域才可访问 
#include <iostream>
using namespace std;

class base
{
public:
	base()
	{
		m_a = 100;
	}
	void func()//父类同名无参函数
	{
		cout << "base的func" << endl;
	}
	void func(int a)//父类同名有参函数
	{
		cout << "base的func(int a)" << endl;
	}
	int m_a;
};
class son :public base
{
public:
	son()
	{
		m_a = 200;
	}
	void func()//子类同名无参函数
	{
		cout << "son的func" << endl;
	}
	int m_a;

};
void test01()
{
	son s;
	cout << s.m_a << endl;//son的同名,直接访问
	cout << s.base::m_a << endl;//父类同名,加作用域

	s.func();//子类的同名函数,直接访问
	s.base::func();//父类的同名函数,加作用域

	//s.func(1);//子类出现和父类同名的成员函数,那子类的成员会隐藏掉父类的所有同名的函数
	s.base::func(1);//加作用域,才可访问
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.6.6继承同名静态成员处理方式

问题?继承中同名的静态成员在子类对象上如何进行访问? 

 静态成员和非静态成员出现同名,处理方式一致

 可以通过对象访问,也可以通过类名方式访问

s.m_a;                        对象访问

s.base::m_a;

s.func();

s.base::func();

son::m_a;                     类名访问

son::base::m_a;

son::func();

son::base::func();

  • 子类对象可以直接访问到子类同名成员
  • 子类对象加作用域可以访问到父类同名成员
  • 当子类和父类有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域才可访问 

 4.6.7多继承语法

 C++允许一个类继承多个类

语法:class 子类 : 继承方式 父类1,继承方式  父类2...

 多继承可能会引发父类中有同名成员出现,需要加作用域区分

C++实际开发中不建议用多继承

 4.6.8菱形继承

 菱形继承概念:

  • 两个派生类继承同一个基类
  • 又有某个类同时继承这两个派生类
  • 这种继承被称为菱形继承或钻石继承

菱形继承会导致子类继承两份相同的数据 ,导致资源浪费以及毫无意义

利用虚继承可以解决菱形继承的问题
继承之前加上关键字 virtual
base 类称为虚基类 

#include <iostream>
using namespace std;

class base//爷爷类
{
public:
	int money;
};
//利用虚继承可以解决菱形继承的问题
//继承之前加上关键字 virtual
//base 类称为虚基类
class son1 :virtual public base//大儿子
{
};
class son2 :virtual public base//二儿子
{
};
class sonson :public son1, public son2//孙子
{
};
void test01()
{
	sonson ss;
	//ss.money = 5000;//不明确
	ss.son1::money = 1000;
	ss.son2::money = 2000;
	cout << "ss.son1::money=="<<ss.son1::money << endl;
	cout << "ss.son2::money=="<<ss.son2::money << endl;
	cout <<"ss.money=="<< ss.money << endl;
	cout << ss.base::money << endl;//2000
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.7多态

 4.7.1多态的基本概念

多态是C++面向对象三大特性之一

多态分为两类

  • 静态多态:函数重载 和 运算符重载属于静态多态,复用函数名
  • 动态多态:派生类和虚函数实现运行时多态 

静态多态和动态多态区别:

  • 静态多态的函数地址早绑定 - 编译阶段确定函数地址
  • 动态多态的函数地址晚绑定 - 运行阶段确定函数地址 

 动态多态满足条件

  1. 有继承关系
  2. 子类要重写父类的虚函数

动态多态使用 

  • 父类的指针或者引用指向子类对象 

Rewriting: function return value type, function name parameter list is exactly the same called rewriting 

Polymorphic advantages:

  1. Code organization is clear
  2. Readable
  3. Facilitate expansion and maintenance in the early and late stages 

 

#include <iostream>
using namespace std;

class Animal
{
public:
	virtual void speak()//虚函数
	{
		cout << "动物说话" << endl;
	}
};

class cat:public Animal
{
public:
	void speak()
	{
		cout<< "猫在说话" << endl;
	}
};
class dog :public Animal
{
public:
	void speak()
	{
		cout << "狗在说话" << endl;
	}
};
//地址早绑定,编译阶段就确定地址
//若想猫说,需要地址晚绑定,只需加virtual,虚函数
void dospeak(Animal &animal)//父类引用指向子类对象Animal &animal=cat
{
	animal.speak();
}
void test01()
{
	cat c;
	dospeak(c);
	dog d;
	dospeak(d);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

Analysis of polymorphic principles

The internal structure of the parent class is inherited by the subclass (there are virtual function pointers and virtual function tables inside)

When a subclass rewrites the virtual function of the parent class, the virtual function table in the subclass will be replaced with the virtual function address of the subclass, and the address of the parent class will not change

Polymorphism occurs when a pointer or reference of a parent class points to an object of a subclass

	cout << "size of animal==" << sizeof(Animal) << endl;//加virtual为8字节,不加为1字节,多了一个指针

 4.7.2 Polymorphic Case 1 Calculator Class

 

4.7.3 Pure virtual functions and abstract classes 

In polymorphism, the implementation of virtual functions in the parent class is usually meaningless, mainly calling the content rewritten by the subclass

So you can change the virtual function to a pure virtual function

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

 When a class has a pure virtual function, this class is also called an abstract class

 Abstract class features :

  • Could not instantiate object
  • The subclass must override the pure virtual function in the abstract class, otherwise it also belongs to the abstract class

 

 4.7.5 Virtual and pure virtual destruction

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

Solution: Change the destructor in the parent class to a virtual destructor to protect the pure virtual destructor

Common features of virtual destructor and pure virtual destructor:

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

The difference between virtual destructor and pure virtual destructor:

  • If it is a pure virtual destructor, the class is an abstract class and cannot instantiate an object

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

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

className::~className(){}

Summarize:

  •  1. Virtual destructor or pure virtual destructor is used to solve the problem of releasing subclass objects through parent class pointers
  • 2. If there is no heap area data in the subclass, it can not be written as virtual destructor or pure virtual destructor
  • 3. A class with a pure virtual destructor is also an abstract class

5. File operation 

The data generated when the program is running is all temporary data, and will be released once the program finishes running

Data can be persisted  through files

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

 There are two file types:

  • 1. Text files - files are stored on your computer as text in ASCII
  • 2. Binary files - files are stored in the computer in binary form of text, and users generally cannot read them directly

Three categories of files are manipulated:

  1. ofstream: write operation
  2. ifstream: read operation
  3. fstream: read and write operations 

5.1 Document text 

 5.1.1 Writing files

The steps to write a file are as follows:

  • 1. Include the header file  #include<fstream>
  • 2. Create a stream object   ofstream ofs;
  • 3. Open the file   ofs.open("file path", open method);
  • 4. Write data  ofs<<"written data";
  • 5. Close the file   ofs.close();

 File open method:

open method explain
ios::in open file for reading
ios::out open file for writing
ios::ate Initial position: end of file
ios::app Appending to write files
ios::trunk If the file exists, delete it first, then create it
ios::binary binary mode

Note: The file opening method can be used in conjunction with the | operator

For example: write files in binary mode ios::binary | ios::out

#include<iostream>
using namespace std;
#include<fstream>//1

void test01()
{
	ofstream ofs;//2

	ofs.open("test.txt",ios::out );//3

	ofs << "姓名:张三" << endl;
	ofs << "性别:男" << endl;//4

	ofs.close();//5
}
int main()
{
	test01();
	system("pause");
	return 0;
}

 Summarize:

  • File operations must include the header file fstream
  • Read files can use ofstream, or fstream class
  • When opening a file, choose to specify the operation file path and the opening method
  • Use << to write data to the file
  • After the operation is complete, close the file

5.1.2 Reading files

The steps to read a file are as follows:

  • 1. Include the header file  #include<fstream>
  • 2. Create a stream object   ifstream ifs;
  • 3. Open the file and judge whether the file is opened successfully   ifs.open("file path", open method);
  • 4. Read data in four ways
  • 5. Close the file   ifs.close();

 

#include<iostream>
using namespace std;
#include<fstream>//1
#include<string>
void test01()
{
	ifstream ifs;//2

	ifs.open("test.txt", ios::in);//3
	if (!ifs.is_open())
	{
		cout << "文件打开失败" << endl;
		return;
	}

	char buf[1024] = { 0 };//第一种方式
	while (ifs >> buf)
	{
		cout << buf << endl;
	}
	 
	char buf[1024] = { 0 };//第二种方式
	while (ifs.getline(buf, sizeof(buf)))
	{
		cout << buf << endl;
	}

	string buf;//第三种方式
	while (getline(ifs, buf))
	{
		cout << buf << endl;
	}

	char c;//第四种方式
	while ((c=ifs.get())!=EOF)//EOF : end  of  file
	{
		cout << c ;
	}
	ifs.close();//5
}
int main()
{
	test01();
	system("pause");
	return 0;
}

Summarize:

  • Read files can use ifstream, or fstream class
  • Use the is_open function to determine whether the file is opened successfully
  • close closes the file

 5.2 Binaries

Read and write files in binary mode

The opening method should be specified as ios::binary 

5.2.1 Writing files 

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

Function prototype: ostream& write (const * buffer, int len);

Parameter explanation: the character pointer buffer points to a storage space in the memory, and len is the number of bytes read and written 

 

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

class person
{
public:
	char m_name[64];
	int m_age;
};
void test01()
{
	ofstream ofs;

	ofs.open("person.txt", ios::out | ios::binary);//二进制

	person p={ "张三", 18 };
	ofs.write((const char *)&p, sizeof(person));

	ofs.close();

}
int main()
{
	test01();
	system("pause");
	return 0;
}

5.2.2 Reading files

 Reading files in binary mode mainly uses the stream object to call the member function read

Function prototype: istream& read(char *buffer, int len);

 Parameter explanation: the character pointer buffer points to a storage space in the memory, and len is the number of bytes read and written

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

class person
{
public:
	char m_name[64];
	int m_age;
};
void test01()
{
	ifstream ifs;

	ifs.open("person.txt", ios::in | ios::binary);//二进制
	if (!ifs.is_open())
	{
		cout << "文件打开失败";
		return;
	}

	person p;
	ifs.read((char*)&p, sizeof(person));

	cout << "姓名" << p.m_name << "  年龄" << p.m_age << endl;
	ifs.close();

}
int main()
{
	test01();
	system("pause");
	return 0;
}

 

Guess you like

Origin blog.csdn.net/weixin_58176527/article/details/127426074