c++学习笔记整理

一、C++

1.C++基础

(1)C++概述

1C++概述
	1.1C++两大编程思想
		1.1.1面向对象
		1.1.2泛型编程
	1.2移植性和标准
		1.2.1ANSI 在1998制定出C++第一套标准
2c++初识
	2.1引入头文件  #include <iostream> 标准输入输出流
	2.2使用标准命名空间   using  namespace  std;
	2.3标准输出流对象 cout << “..” << 1234 << 3.14 << endl;
	2.4面向对象三大特性
		2.4.1封装、继承、多态
3双冒号作用域运算符
	3.1::代表作用域  如果前面什么都不添加 代表全局作用域

(2)namespace命名空间

1)命名空间用途:解决名称冲突
2)命名空间下可以存放 : 变量、函数、结构体、类…
3)命名空间必须要声明在全局作用域
4)命名空间可以嵌套命名空
5)命名空间是开放的,可以随时将新成员添加到命名空间下
6)命名空间可以匿名的
7)命名空间可以起别名
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "game1.h"
#include "game2.h"
using namespace std;

//1、命名空间用途: 解决名称冲突
void test01() {
	Gotest02::gotest();
}

//2、命名空间下 可以放  变量、函数、结构体、类...
namespace A
{
	int m_A;
	void func();
	struct Person
	{};
	class Animal
	{};
}

//3、命名空间 必须要声明在全局作用域下
void test02()
{
	//namespace B{}; 不可以命名到局部作用域
}

//4、命名空间可以嵌套命名空间
namespace B
{
	int m_A = 10;
	namespace C
	{
		int m_A = 20;
	}
}
void test03()
{
	cout << "B空间下的m_A = " << B::m_A << endl;
	cout << "C空间下的m_A = " << B::C::m_A << endl;
}

//5、命名空间是开放的,可以随时给命名空间添加新的成员
namespace B
{
	int m_B = 100;
}

void test04()
{
	cout << "B空间下的m_A = " << B::m_A << endl; //10
	cout << "B空间下的m_B = " << B::m_B << endl; //100
}

//6、命名空间可以是匿名的
namespace
{
	int m_C = 1000;
	int m_D = 2000;
	//当写的命名空间的匿名的,相当于写了  static int m_C = 1000; static int m_D = 2000;
}

void test05()
{
	cout << "m_C = " << m_C << endl;
	cout << "m_D = " << ::m_D << endl;
}

//7、命名空间可以起别名
namespace veryLongName
{
	int m_E = 10000;
	void func()
	{
		cout << "aaa" << endl;
	}
}

void test06()
{
	namespace veryShortName = veryLongName;
	cout << veryShortName::m_E << endl;
	cout << veryLongName::m_E << endl;

}

int main()
{
	test01();
	test03();
	test04();
	test05();
	test06();
	system("pause");
	return EXIT_SUCCESS;
}

(3)using声明以及using编译指令

1、using声明
	1.1、using KingGlory::sunwukongId
	1.2、当using声明与 就近原则同时出现,出错,尽量避免
2using编译指令
	2.1、using namespace KingGlory;
	2.2、当using编译指令  与  就近原则同时出现,优先使用就近
	2.3、当using编译指令有多个,需要加作用域 区分
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

namespace KingGlory
{
	int SunWuKongId = 1;

}
namespace LOL
{
	int SunWuKongId = 3;
}
void test01()
{
	/*int SunWuKongId = 2;*/
	//1.using声明
	using KingGlory::SunWuKongId;

	//当using声明与就近原则同时出现时,出错,尽量避免
	cout << SunWuKongId << endl;
}
void test02()
{
	int SunWuKongId = 2;
	//2.using编译指令
	using namespace KingGlory;
	using namespace LOL;
	//当using的编译指令与 就近原则同时出现时,优先使用就近原则
	//当using编译指令有多个,需要加作用域 区分
	cout << KingGlory::SunWuKongId << endl;
	cout << LOL::SunWuKongId << endl;
}
int main()
{
	test01(); //1
	test02(); //2
	system("pause");
	return EXIT_SUCCESS;
}

(4)C++对C语言增强以及扩展

1、全局变量检测增强
	1.1、int a ;
	1.2、int a = 10;  C下可以,C++重定义
2、函数检测增强
	2.1、函数的返回值
	2.2、形参类型
	2.3、函数调用参数个数
3、类型转换检测增强
	3.1、char * p = (char *)malloc(64)  C++下必须等号左右一致类型
4、struct 增强
	4.1、C++可以在结构体中放函数
	4.2、创建结构体变量   可以简化关键字struct
5、bool数据类型扩展
	5.1、C++才有bool类型
	5.2、代表真 --- 1 true  假 ---- 0 false
	5.3、sizeof  = 1
6、三目运算符增强
	6.1、C语言下返回的是值
	6.2、C++语言下返回的是变量
7、const增强
	7.1、C语言下
		7.1.1、全局const   直接修改 失败  间接修改  语法通过,运行失败
				受到常量区保护,运行修改失败
		7.1.2、局部 const  直接修改  失败  间接修改  成功
				分配到栈上
		7.1.3、在C语言下 const是伪常量,不可以初始化数组
	7.2、C++语言下
		7.2.1、全局 const  和C结论一样
		7.2.2、局部 const  直接修改失败   间接修改 失败  
		7.2.3、C++const可以称为常量
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <Windows.h>

//1、全局变量检测增强,C++检测出重定义
int a;
int a = 10;

//2、函数检测增强 
int getRectS(int w, int h)
{
	//return w * h;
}
void test01()
{
	printf("%d\n", getRectS(10, 10, 10));
}

//3、类型转换检测增强
void test02()
{
	char* p = malloc(64);
}

//4、struct增强
struct Person
{
	int age;
	//void func(); C语言下 结构体不可以有函数
};
void test03()
{
	struct Person p; //创建结构体变量时候,必须加关键字struct
	p.age = 100;
}

//5、bool类型扩展  C语言下 没有这个类型
//bool a;

//6、三目运算符增强
void test04()
{
	//?:
	int a = 10;
	int b = 20;

	printf("ret = %d\n", a > b ? a : b);

	*(a > b ? &a : &b) = 100;  //C语言下 返回的是值  20 = 100

	printf("a = %d\n", a);
	printf("b = %d\n", b);

}

//7、const增强
//全局const
const int m_A = 100; // 受到常量区保护,运行修改失败

void test05()
{
	//直接修改
	//m_A = 200;
	// 
	//间接修改
	//int * p = &m_A;
	//*p = 200;   修改失败

	//局部const
	const int m_B = 100; //分配到栈上
	//m_B = 200;  编译不通过
	int* p = &m_B;
	*p = 200;

	printf("%d\n", m_B);

	//int arr[m_B]; 在C语言下 const是伪常量,不可以初始化数组

}
int main(void)
{
	test01();
	test02();
	test03();
	test04();
	test05();
	system("pause");
	return EXIT_SUCCESS;
}
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//1、全局变量检测增强,c++检测出
int a;
//int a = 10;

//2、函数检测增强 返回值检测、形参类型检测、函数调用参数个数
int getRectS(int w, int h)
{
	return w * h;
}

void test01()
{
	printf("%d\n",getRectS(10,10));
}
//3、类型转换检测增强
void test02()
{
	char* p = (char*)malloc(64);
}

//4、struct增强  C++可以放函数,创建结构体变量,可以简化关键字 struct
struct Person
{
	int age;
	void func()
	{
		age++;
	}
};

void test03()
{
	Person p;
	p.age = 17;
	p.func();
	cout << "p的age = " << p.age << endl;
}

//5、bool类型扩展  C语言下 没有这个类型  C++有bool类型
bool flag = true; // bool类型 代表  真和假   true  ---- 真(1)    false  ---- 假(0)

void test04()
{
	cout << sizeof(bool) << endl; //结果是1个字节
	//flag = false;
	//flag = 100; //将非0的数都转为1
	cout << flag << endl;
}

//6、三目运算符增强
void test05()
{
	//?:
	int a = 10;
	int b = 20;

	printf("ret = %d\n", a > b ? a : b);

	(a < b ? a : b) = 100; // C++下返回的是变量  b = 100

	printf("a = %d\n", a);
	printf("b = %d\n", b);
}

//7、const增强
//全局const   和C语言结论一致
const int m_A = 100;
void test06()
{
	//直接修改
	//m_A = 200;
	// 
	//间接修改
	//int * p = (int *)&m_A;

	//*p = 200;

	//局部const
	const int m_B = 100;
	//m_B = 200;  编译出错
	int* p = (int*)&m_B;
	*p = 200;
	cout << "m_B = " << m_B << endl;

	int arr[m_B]; //C++下const修饰的变量 称为常量 ,可以初始化数组

}

int main()
{
	test01();	//100
	test02();
	test03();
	test04();
	system("pause");
	return EXIT_SUCCESS;
}

(5)const相关

1.const 链接属性
	1.1、C语言下const修饰的全局变量默认是外部链接属性
	1.2、C++下const修饰的全局变量默认是内部链接属性,可以加extern 提高作用域
2.const分配内存情况
	2.1、对const变量 取地址 ,会分配临时内存  
	2.2、使用普通变量  初始化 const变量;const int a=10这样不能修改
	2.3、对于自定义数据类型
3.尽量用const代替define
	define出的宏常量,没有数据类型、不重视作用域
c++const修饰的局部变量分配到符号表上

//局部const
const int m_B=100;
//m_B=200;  直接修改出错
int *p=(int *)&m_B;
*p = 200;

int *P = (int *)&m_B;
//当对const修饰的局部变量取地址的时候,编译器给变量分配临时内存空间temp,p指针指向的是temp
int temp = m_B;
int *p =&temp;
*p = 200;
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//1、对const变量 取地址 ,会分配临时内存  
void test01()
{
	const int a = 10;
	int* p = (int*)&a;
}

//2、使用普通变量  初始化 const变量
void test02()
{
	int a = 10;
	const int b = a; 

	int* p = (int*)&b;
	*p = 1000;

	cout << "b = " << b << endl;	//1000

}

//3、对于自定义数据类型 
struct Person
{
	string m_Name;
	int m_Age;
};
void test03()
{
	const Person p;
	//p.m_Age = 10;  直接修改失败

	Person* pp = (Person*)&p;
	(*pp).m_Name = "Tom";
	pp->m_Age = 10;

	cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}

int main() {
	//test02();
	test03();
	system("pause");
	return EXIT_SUCCESS;
}

(6)引用

引用
1、目的:起别名
2、语法: 类型(与原名类型必须一致)   &别名  =  原名
3、引用必须要初始化
4、引用一旦初始化后,就不可以引向其他变量
5、建立对数组引用
	5.1、直接建立引用
	5.2、int arr[10];
	5.3、int(&pArr)[10] = arr;
6、先定义出数组类型,再通过类型 定义引用
	6.1、typedef int(ARRAY_TYPE)[10];
	6.2、ARRAY_TYPE & pArr2 = arr;
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//引用基本语法 :类型  &别名 = 原名
void test01() {
	int a = 10;
	int& b = a;
	b = 100;
	cout << "a= " << a << endl;  //100
	cout << "b = " << b << endl; //100
}

void test02() {
	int a = 10;
	//int& b;   //引用必须要初始化

	int& b = a;

	//引用一旦初始化后,就不可以引向其他变量

	int  c = 100;
	b = c;  //赋值
	cout << "a = " << a << endl;  //100
	cout << "b = " << b << endl; //100
	cout << "c = " << c << endl; //100
}

//对数组建立引用
void test03() {
	//1、直接建立引用
	int arr[10];
	int(&pArr)[10] = arr;
	for (int i = 0; i < 10; i++)
		arr[i] = 100 + i;
	for (int i = 0; i < 10; i++)
		cout << pArr[i] <<endl;
	
	cout << endl;
	//2、先定义出数组类型,再通过类型 定义引用
	typedef int(ARRAY_TYPE)[10];
	//类型  &别名 = 原名
	ARRAY_TYPE& pArr2 = arr;

	for (int i = 0; i < 10; i++)
	{
		cout << pArr2[i] << endl;
	}
}

int main()
{
	//test01();
	//test02();
	test03();
	system("pause");
	return EXIT_SUCCESS;
}

(7)参数的传递方式

1参数的传递方式
	1.1、值传递
	1.2、地址传递
	1.3、引用传递
2注意事项
	2.1、引用必须引一块合法内存空间
	2.2、不要返回局部变量的引用
	2.3、当函数返回值是引用时候,那么函数的调用可以作为左值进行运算
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//1、值传递
void mySwap01(int a, int b)
{
	int temp = a;
	a = b;
	b = temp;
	/*cout << ":::a = " << a << endl;
	cout << ":::b = " << b << endl;*/
}

//2、地址传递
void mySwap02(int* a, int* b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}

//3、引用传递
void mySwap03(int& a, int& b) // int &a = a; int &b = b;
{
	int temp = a;
	a = b;
	b = temp;
}

void test01()
{
	int a = 10;
	int b = 20;
	//mySwap01(a, b);
	//mySwap02(&a, &b);

	mySwap03(a, b);

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}

int& func()
{
	int a = 10;
	return a;
}

//引用注意事项
void test02()
{
	//1、引用必须引一块合法内存空间
	//int &a = 10;   正确写法int b=10;  int &a=b;

	//2、不要返回局部变量的引用
	int& ref = func();
	cout << "ref = " << ref << endl;    //ref = 10
	cout << "ref = " << ref << endl;	//ref = 2033289608
}

int& func2()
{
	static int a = 10;
	return a;
}

void test03()
{
	int& ref = func2();
	cout << "ref = " << ref << endl; //10
	cout << "ref = " << ref << endl; //10
	cout << "ref = " << ref << endl; //10
	cout << "ref = " << ref << endl; //10
	//当函数返回值是引用,那么函数的调用可以作为左值
	func2() = 1000;

	cout << "ref = " << ref << endl; //1000
}

int main() {
	test01();
	test02();
	test03();
	system("pause");
	return EXIT_SUCCESS;
}

(8)指针的引用

指针的引用
1、利用引用可以简化指针
2、可以直接用同级指针的 引用  给同级指针分配空间
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

struct Person
{
	int age;
};

void allocateSpace(Person** p)
{
	//p指向指针的指针    *p  指针 指向的是person 本体   **p  person本体
	*p = (Person*)malloc(sizeof(Person));
	(*p)->age = 10;

}

void test01()
{
	Person* p = NULL;
	allocateSpace(&p);

	cout << "p.age = " << p->age << endl;
}

void allocateSpace2(Person*& pp) // Person * &pp = p;
{
	pp = (Person*)malloc(sizeof(Person));
	pp->age = 20;
}

void test02()
{
	Person* p = NULL;
	allocateSpace2(p);
	cout << "p.age = " << p->age << endl;
}

int main() {

	test01();	//10
	test02();  //20
	system("pause");
	return EXIT_SUCCESS;
}

(9)常量的引用

常量的引用
1、const int &ref = 10;
2、 // 加了const之后,  相当于写成   int temp = 10;  const int &ref = temp;
3、常量引用的使用场景 修饰函数中的形参,防止误操作
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

void test01() {
	//int& ref = 10; 编译出错
	
	// 加了const之后, 相当于写成   int temp = 10;  const int &ref = temp;
	const int& ref = 10;

	int* p = (int *)&ref;
	*p = 10000;
	cout << ref << endl;	//10000
}

void showValue(const int& a)
{
	//a = 100000;

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

}
//常量引用的使用场景 修饰函数中的形参,防止误操作
void test02()
{
	int a = 100;
	showValue(a);

}

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

2.面向对象类

(1)类

设计一个类,求圆的周长
1、class  + 类名 {  成员变量  成员函数   }
2、公共权限  public  
3、设计成员属性
	3.1、半径  int m_R
4、设计成员函数
	4.1、获取圆周长  int calculateZC(){}
	4.2、获取圆半径  int getR()
	4.3、设置圆半径  void setR()
5、通过类创建对象过程   称为 实例化对象

(2)内联函数

内联函数
1、内联函数引出---宏缺陷
	1.1、宏缺陷: 
		1.1.1、必须要加括号保证运算完整
		1.1.2、即使加括号,有些运算依然与预期结果不符
	1.2、普通函数不会出现缺陷
2、C++提供 内联函数代替宏函数
3、关键字 inline
4、在函数声明和实现中同时加入关键字  inline 才称为内联
5、在成员函数前  都隐式加了关键字inline
6、有些特殊情况下 ,写了关键字inline也不会按照内联方式处理
	6.1、出现循环
	6.2、出现判断
	6.3、函数体过于庞大
	6.4、对函数进行取地址
7、总结: 内联函数只是给编译器一个建议,但是编译器不一定接受这个建议,好的编译器会自己给短小的函数前加上关键字inline
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//宏缺陷1 : 必须要加括号保证运算完整
#define MYADD(x,y) x+y
#define MYADD1(x,y) ((x)+(y))
void test01()
{
	int a = 10;
	int b = 20;
	int res = MYADD(a, b) * 20;
	int ans = MYADD1(a, b) * 20;
	cout << "MYADD="<<res << endl;	//410
	cout << "MYADD1="<<ans<< endl;	//600
}
//宏缺陷2:  即使加了括号,有些运算依然与预期不符

#define MYCOMPARE(a,b)  (((a) < (b)) ? (a) : (b))

//普通函数 不会出现与预期结果不符的问题
void myCompare(int a, int b)
{
	int ret = a < b ? a : b;
	cout << "ret = " << ret << endl;
}

void test02()
{
	int a = 10;
	int b = 20;

	myCompare(++a, b);

	//int ret = MYCOMPARE(++a, b); //预期是 11 结果变为12   (((++a) < (b)) ? (++a) : (b))

	//cout << "ret = " << ret << endl;

}

//内联函数
//函数的声明和实现必须同时加关键字 inline  才算内联函数
//内联函数 好处  :解决宏缺陷,本身是一个函数,带来宏优势,以空间换时间,在适当的时候做展开
inline void func();
inline void func() {};
//类内部的成员函数 在函数前都隐式加了关键字 inline

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

(3)函数的默认参数和占位参数

1、默认参数
	1.1、可以给函数的形参添加默认值
	1.2、语法  形参  类型 变量  = 默认值 
	1.3、int func(int a, int b = 10 , int c = 10)
	1.4、注意事项 ,如果有一个位置有了默认参数,那么从这个位置起,从左到右都必须有默认值
	1.5、函数的声明和实现 只能有一个 提供默认参数,不可以同时加默认参数
2、占位参数
	2.1、只写一个类型进行占位,调用时候必须要传入占位值
	2.2、void func2(int a , int = 1)
	2.3、占位参数也可以有默认值
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;


//默认参数 语法  形参  类型 变量  = 默认值
//注意事项 ,如果有一个位置有了默认参数,那么从这个位置起,从左到右都必须有默认值
int func(int a, int b = 10, int c=10)
{
	return  a + b + c;
}

void test01()
{
	cout << func(20, 20) << endl;	//50
}

//函数的声明和实现 只能有一个 提供默认参数,不可以同时加默认参数
void myFunc(int a = 10, int b = 10);
//void myFunc(int a=10, int b=10) {};
void myFunc(int a, int b) {};


//占位参数  只写一个类型进行占位,调用时候必须要传入占位值
//占位参数 用途? 目前没用
void func2(int a, int = 1)
{

}

void test02()
{
	func2(10);
}

int main() {

	test01();
	system("pause");
	return EXIT_SUCCESS;
}

(4)函数重载

函数重载
1、满足条件
	1.1、同一个作用域下
	1.2、函数名称相同
	1.3、函数参数个数、类型、顺序不同
2、函数的返回值  不可以作为重载条件
3、注意事项
	3.1、加const和不加const的引用可以作为重载条件
	3.2、函数重载碰到默认参数  注意避免二义性出现
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//函数重载条件
//1、在同一个作用域
//2、函数名称相同
//3、参数个数、类型、顺序不同

//class Person
//{
//public:
//	void func()   成员函数 而不是全局函数
//	{
//	}
//};

void func()
{
	cout << "func()调用" << endl;
}

void func(int a)
{
	cout << "func(int a)调用" << endl;
}

void func(double a)
{
	cout << "func(double a)调用" << endl;
}

void func(int a, double b)
{
	cout << "func(int a ,double b)调用" << endl;
}

void func(double a, int b)
{
	cout << "func(double a, int b)调用" << endl;
}

//返回值可不可以作为函数重载的条件 答案:不可以
//int func(int a, double b)
//{
//	cout << "func(int a ,double b)调用" << endl;
//}

void test01()
{
	func(1, 3.14);
}

//函数重载中 引用两个版本
//void myFunc(int a)
//{
//	cout << "myfunc(int a )调用" << endl;
//}
void myFunc(int& a) // int & a  = 10;
{
	cout << "myfunc(int &a )调用" << endl;
}
void myFunc(const int& a) // const int &a = 10;
{
	cout << "myfunc( const int &a )调用" << endl;
}

void test02()
{
	int a = 10;
	//myFunc(a);//需要避免二义性出现

}

//函数重载碰到默认参数  注意避免二义性出现
void func2(int a, int b = 10)
{

}

void func2(int a)
{

}

void test03()
{
	//func2(10); //出现二义性
}

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

	system("pause");
	return EXIT_SUCCESS;
}

(5)extern C 浅析

1、用途:在C++中调用C语言文件
2、C++中有函数重载,会对函数名称做修饰,导致调用C语言的函数链接失败
3、利用extern C可以解决问题
	3.1、方法1:
		3.1.1、在C++代码中加入
		3.1.2、告诉编译器  show函数用C语言方式 做链接
		3.1.3、 extern "C" void show();
	3.2、方法2:
		在C语言的头文件中加入6行代码
			#ifdef __cplusplus  // 两个下划线  __  c plus plus
			extern "C" {
			#endif
				
			#ifdef __cplusplus  // 两个下划线  __  c plus plus
			}
			#endif	
test.h
#ifdef __cplusplus  // 两个下划线  __  c plus plus
extern "C" {
#endif

#include <stdio.h>

	void show();

#ifdef __cplusplus
}
#endif
test.c
#include "test.h"
void show()
{
	printf("hello\n");
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include "test.h"
using namespace std;

//告诉编译器  show函数用C语言方式 做链接
//extern "C" void show();	//#include "test.h"不用

void test01()
{
	show();//_Z4showv;在C++中有函数重载会修饰函数名,但是show是c语言文件,因此链接失败
}
int main()
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

3.面向对象类的封装

(1)封装

1、C语言的封装
	1.1、缺陷 将属性和行为分离
2、C++语言的封装
	2.1、将属性和行为作为一个整体,来表现生活中的事物
	2.2、将属性和行为  加以权限控制
3、访问权限
	3.1、公共权限 public     类内 类外  都可以访问
	3.2、私有权限 private    类内可以访问  类外不可以访问
	3.3、保护权限 protected  类内可以访问  类外不可以访问
4、class 默认权限  私有权限  而 struct默认权限是 公共权限
5、尽量将成员属性设置为私有
	5.1、自己可以控制读写权限
	5.2、可以对设置内容 加有效性验证
C++中struct和class的区别
1.相同点
	两者都拥有成员函数、公有和私有部分
	任何可以使用class完成的工作,同样可以使用struct完成
2.不同点
	两者中如果不对成员不指定公私有,struct默认是公有的,class则默认是私有的
	class默认是private继承,而struct模式是public继承
	
在C++中,class和struct做类型定义是只有两点区别:
1.默认继承权限不同,class继承默认是private继承,⽽struct默认是public继承
2.class还可⽤于定义模板参数,像typename,但是关键字struct不能同于定义模板参数 C++保留struct关键
字,原因
3.保证与C语⾔的向下兼容性,C++必须提供⼀个struct
4.C++中的struct定义必须百分百地保证与C语⾔中的struct的向下兼容性,把C++中的最基本的对象单元规定为
class⽽不是struct,就是为了避免各种兼容性要求的限制
5.对struct定义的扩展使C语⾔的代码能够更容易的被移植到C++中	
	
	
引申:C++和C的struct区别
1.C语言中:struct是用户自定义数据类型(UDT);C++中struct是抽象数据类型(ADT),支持成员函
数的定义,(C++中的struct能继承,能实现多态)
2.C中struct是没有权限的设置的,且struct中只能是一些变量的集合体,可以封装数据却不可以隐藏数
据,而且成员不可以是函数
3.C++中,struct增加了访问权限,且可以和类一样有成员函数,成员默认访问说明符为public(为了与
C兼容)
4.struct作为类的一种特例是用来自定义数据结构的。一个结构标记声明后,在C中必须在结构标记前加
上struct,才能做结构类型名(除:typedef struct class{};);C++中结构体标记(结构体名)可以直接
作为结构体类型名使用,此外结构体struct在C++中被当作类的一种特例
访问权限
public  公共权限    成员 类内  类外 都可以访问	 
private 私有权限    成员 类内  可以访问  类外  不可以访问  儿子不可以访问父亲的private权限内容protected 保护权限  成员 类内  可以访问  类外  不可以访问  儿子可以访问父亲的protected权限内容
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

struct  Person
{
	//公共权限
public:
	char name[64];
	int age;

	void PersonEat()
	{
		printf("%s在吃人饭\n", name);
	}
};

struct Dog
{
	//公共权限
public:
	char name[64];
	int age;
	void DogEat()
	{
		printf("%s在吃狗粮\n", name);
	}
};

//C++封装 理念:  将属性和行为作为一个整体,来表现生活中的事物
//第二次理念: 将属性和行为  加以权限控制

void test01()
{
	struct Person p;
	strcpy(p.name, "老王");
	p.PersonEat();
	//p.DogEat();
}

//struct和class 区别?
//class 默认权限  私有权限  而 struct默认权限是 公共权限
//访问权限
// public  公共权限    成员 类内  类外 都可以访问	 
// private 私有权限    成员 类内  可以访问  类外  不可以访问  儿子不可以访问父亲的private权限内容
// protected 保护权限  成员 类内  可以访问  类外  不可以访问  儿子可以访问父亲的protected权限内容

class Person2
{
public:
	string m_Name; //公共权限

protected:
	string m_Car;  //保护权限

private:
	int  m_pwd;  //私有权限

public:
	void func()
	{
		m_Name = "张三";
		m_Car = "拖拉机";
		m_pwd = 123456;
	}
};

void test02()
{
	Person2 p;
	p.m_Name = "李四"; //公共权限  类外可以访问
//	p.m_Car = "劳斯莱斯"; //保护权限  类外访问不到
//	p.m_pwd = 123; //私有权限  类外不可以访问
}

int main() {
	test01();
	system("pause");
	return EXIT_SUCCESS;
}
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>
class Person
{
public:
	//设置姓名
	void setName(string name)
	{
		m_Name = name;
	}
	//获取姓名
	string getName()
	{
		return m_Name;
	}

	//获取年龄
	int getAge()
	{
		return m_Age;
	}

	//设置年龄
	void setAge(int age)
	{
		if (age < 0 || age > 150)
		{
			cout << "你这个老妖精" << endl;
			return;
		}
		m_Age = age;
	}

	//设置情人
	void setLover(string lover)
	{
		m_Lover = lover;
	}

private:
	string m_Name;  //姓名  可读可写
	int m_Age = 18;      //年龄  可读 可写(0 ~ 150之间)
	string m_Lover; //情人  只写
};

void test01()
{
	Person p;
	//可以将char * 隐式类型转换为 string
	p.setName("张三");
	cout << "姓名: " << p.getName() << endl;

	//获取年龄
	p.setAge(100);
	cout << "年龄: " << p.getAge() << endl;

	//设置情人
	p.setLover("苍井");

	//cout << "张三情人是:"<< p.m_Lover <<endl; //情人是只写权限  外部访问不到

}

//将成员属性都设置为私有好处:自己可以控制读写权限
//可以对设置内容 加有效性验证
int main() {
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

(2)案例

1设计立方体类案例
	1.1设计class Cube
	1.2属性
		1.2.1长宽高
	1.3行为
		1.3.1设置长宽高
		1.3.2获取长宽高
		1.3.3获取面积
		1.3.4获取体积
	1.4通过全局函数和成员函数 判断两个立方体是否相等

2点和圆关系案例
	2.1设计点和圆类
	2.2点类 Point
		2.2.1属性  x  y
		2.2.2行为  设置 获取 x y
	2.3圆类 Circle
		2.3.1属性: 圆心 Point m_Center  半径  m_R;
		2.3.2行为: 设置 获取  半径  圆心
	2.4通过成员函数 和 全局函数 判断点和圆关系
	2.5分文件编写 点和圆类
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

/*
设计立方体类(Cube),求出立方体的面积( 2*a*b + 2*a*c + 2*b*c )和体积( a * b * c),
分别用全局函数和成员函数判断两个立方体是否相等。
*/

class Cube
{
public:
	//设置长宽高
	void setL(int l)
	{
		m_L = l;
	}
	void setW(int w)
	{
		m_W = w;
	}
	void setH(int h)
	{
		m_H = h;
	}
	//获取长宽高
	int getL()
	{
		return m_L;
	}
	int getW()
	{
		return m_W;
	}
	int getH()
	{
		return m_H;
	}

	//求面积 
	int calculateS()
	{
		return 2 * m_L * m_W + 2 * m_W * m_H + 2 * m_L * m_H;
	}

	//求体积
	int calculateV()
	{
		return m_L * m_W * m_H;
	}

	//利用成员函数 判断立方体是否相等
	bool compareCubeByClass(Cube& c)
	{
		return m_L == c.getL() && m_W == c.getW() && m_H == c.getH();
	}

private:
	int m_L; //长
	int m_W; //宽
	int m_H; //高
};

//利用全局函数 判断两个立方体是否相等
bool compareCube(Cube& c1, Cube& c2)
{
	/*if (c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH())
	{
	return true;
	}
	return false;*/

	return c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH();
}

void test01()
{
	Cube c1;
	c1.setL(10);
	c1.setW(10);
	c1.setH(10);

	cout << "c1面积为: " << c1.calculateS() << endl; //600
	cout << "c1体积为: " << c1.calculateV() << endl; //1000

	Cube c2;
	c2.setL(10);
	c2.setW(10);
	c2.setH(10);

	//利用全局函数判断 c1 和 c2是否相等

	bool ret = compareCube(c1, c2);
	if (ret)
	{
		cout << "c1 与 c2 相等" << endl;
	}
	else
	{
		cout << "c1 与 c2 不相等" << endl;
	}

	//利用成员函数 判断  c1 和 c2是否相等
	ret = c1.compareCubeByClass(c2);
	if (ret)
	{
		cout << "成员函数判断:c1 与 c2 相等" << endl;
	}
	else
	{
		cout << "成员函数判断:c1 与 c2 不相等" << endl;
	}
}

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

(3)构造函数和析构函数

1构造函数
	1.1//没有返回值  不用写void
	1.2//函数名 与 类名相同
	1.3//可以有参数  ,可以发生重载
	1.4//构造函数 由编译器自动调用一次 无须手动调用
2析构函数
	2.1//没有返回值   不用写void
	2.2函数名 与类名相同  函数名前 加 ~
	2.3不可以有参数 ,不可以发生重载
	2.4析构函数 也是由编译器自动调用一次,无须手动调用
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
public: //构造和析构必须要声明在全局作用域

	//构造函数
	//没有返回值  不用写void
	//函数名 与 类名相同
	//可以有参数  ,可以发生重载
	//构造函数 由编译器自动调用一次 无须手动调用
	Person()
	{
		cout << "Person的构造函数调用" << endl;
	}
	
	//析构函数
	//没有返回值   不用写void
	//函数名 与类名相同  函数名前 加 ~
	//不可以有参数 ,不可以发生重载
	//析构函数 也是由编译器自动调用一次,无须手动调用
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}
};

void test01()
{
	Person p;
}

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

(4)构造函数的分类和调用

1分类
	1.1按照参数分类: 有参  无参(默认)
	1.2按照类型分类: 普通  拷贝构造 ( const  Person & p  )
2调用
	2.1括号法
	2.2显示法
	2.3隐式法
3注意事项
	3.1不要用括号法 调用无参构造函数  Person p3();  编译器认为代码是函数的声明
	3.2不要用拷贝构造函数 初始化 匿名对象 Person(p3); 编译器认为 Person p3对象实例化  如果已经有	p3  p3就重定义
4匿名对象  特点: 当前行执行完后 立即释放
#define _CRT_SECURE_NO_WARNINGS
#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)
	{
		cout << "Person的拷贝构造函数调用" << endl;
		m_Age = p.m_Age;
	}

	//析构函数
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}
	int m_Age;
};

//构造函数的调用
void test01()
{
	//Person p;

	//1、括号法
	//Person p1(10);
	//Person p2(p);

	//注意事项一 
	//不要用括号法 调用无参构造函数  Person p3();  编译器认为代码是函数的声明

	//2、显示法
	//Person p3 = Person(10); //有参构造
	//Person p4 = Person(p3); //拷贝构造

	//Person(10); //匿名对象  特点: 当前行执行完后 立即释放

	//cout << "aaa" << endl;

	//注意事项二
	//不要用拷贝构造函数 初始化 匿名对象 
	//Person(p3); 编译器认为 Person p3对象实例化  如果已经有p3  p3就重定义

	//3、隐式法  
	Person p5 = 10; //Person p5 = Person(10);
	Person p6 = p5;
}

int main() {
	test01();
	//Person p(18);
	//Person p2(p);
	//cout << "p2的年龄: " << p2.m_Age << endl;
	system("pause");
	return EXIT_SUCCESS;
}

(5)拷贝构造函数的调用时机

拷贝构造函数的调用时机
1、用已经创建好的对象来初始化新的对象
2、值传递的方式 给函数参数传值
3、以值方式 返回局部对象
#define _CRT_SECURE_NO_WARNINGS
#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)
	{
		cout << "Person的拷贝构造函数调用" << endl;
		m_Age = p.m_Age;
	}

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

	int m_Age;

};

//1、用已经创建好的对象来初始化新的对象
void test01()
{
	Person p1(18);

	Person p2 = Person(p1);

	cout << "p2的年龄:" << p2.m_Age<< endl;

}

//2、值传递的方式 给函数参数传值
void doWork(Person p)
{

}
void test02()
{
	Person p1(100);

	doWork(p1);

}

//3、以值方式 返回局部对象
Person doWork2()
{
	Person p;
	return p;
}

void test03()
{
	Person p = doWork2();
}

/*	
	编译器优化代码后 release版本代码类似以下:
	void doWork2( Person &p ){};

	Person p;
	doWork2(p);
*/

int main(){
	//test01();
	//test02();
	test03();
	system("pause");
	return EXIT_SUCCESS;
}

(6)构造函数的调用规则

构造函数的调用规则
1、编译器会给一个类 至少添加3个函数    默认构造(空实现)   析构函数(空实现)   拷贝构造(值拷贝)
2、如果我们自己提供了 有参构造函数,编译器就不会提供默认构造函数,但是依然会提供拷贝构造函数
3、如果我们自己提供了 拷贝构造函数,编译器就不会提供其他构造函数
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//1、编译器会给一个类 至少添加3个函数    默认构造(空实现)   析构函数(空实现)   拷贝构造(值拷贝)
//2、如果我们自己提供了 有参构造函数,编译器就不会提供默认构造函数,但是依然会提供拷贝构造函数
//3、如果我们自己提供了 拷贝构造函数,编译器就不会提供其他构造函数
class Person
{
public:

	Person()
	{
		cout << "默认构造函数调用" << endl;
	}

	Person(int age)
	{
		m_Age = age;
		cout << "有参构造函数调用" << endl;
	}

	Person(const Person &p)
	{
		m_Age = p.m_Age;
		cout << "拷贝构造函数调用" << endl;
	}

	~Person()
	{
		cout << "析构函数调用" << endl;
	}
	int m_Age;
};

void test01()
{
	Person p1;//提供拷贝构造后,要自己提供默认构造,否则出错
	p1.m_Age = 20;

	Person p2(p1);

	cout << "p2的年龄为: " << p2.m_Age << endl;

}

int main(){

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(7)深拷贝与浅拷贝的问题以及解决

1 如果有属性开辟到堆区,利用编译器提供拷贝构造函数会调用浅拷贝带来的析构重复释放堆区内存的问题
2 利用深拷贝解决浅拷贝问题
3 自己提供拷贝构造函数,实现深拷贝
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
public:

	Person(char* name, int age)
	{
		m_Name = (char*)malloc(strlen(name) + 1);

		strcpy(m_Name, name);

		m_Age = age;
	}

	Person(const Person& p)
	{
		m_Name = (char*)malloc(strlen(p.m_Name) + 1);
		strcpy(m_Name, p.m_Name);
		m_Age = p.m_Age;
	}

	~Person()
	{
		if (m_Name != NULL)
		{
			cout << "Person析构调用" << endl;
			free(m_Name);
			m_Name = NULL;
		}
	}

	char* m_Name; //姓名
	int m_Age;     //年龄
};


void test01()
{
	char name[] = "zky";
	Person p(name,18);
	cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;

	Person p2(p);
	cout << "姓名: " << p2.m_Name << " 年龄: " << p2.m_Age << endl;
}

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(8)初始化列表

1可以利用初始化列表语法 对类中属性进行初始化
2语法:构造函数名称后  : 属性(值), 属性(值)...
	2.1	Person(int a, int b, int c) : m_A(a), m_B(b), m_C(c)
#define _CRT_SECURE_NO_WARNINGS
#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() :m_A(10), m_B(20), m_C(30)
	//{
	//}

	//构造函数名称后  : 属性(值), 属性(值)...
	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 EXIT_SUCCESS;
}

(9)类对象作为类中成员

当其他类对象 作为本类成员,先构造其他类对象,再构造自身,析构的顺序和构造相反
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>

class Phone
{
public:

	Phone(string pName)
	{
		cout << "phone 的有参构造调用" << endl;
		m_PhoneName = pName;
	}
	~Phone()
	{
		cout << "phone 的析构函数调用" << endl;
	}
	string m_PhoneName;
};

class Game
{
public:
	Game(string gName)
	{
		cout << "Game 的有参构造调用" << endl;
		m_GameName = gName;
	}
	~Game()
	{
		cout << "Game 的析构函数调用" << endl;
	}
	string m_GameName;
};

class Person
{
public:

	Person(string name, string pName, string gName) : m_Name(name), m_Phone(pName), m_Game(gName)
	{
		cout << "Person 的有参构造调用" << endl;
	}

	void PlayGame()
	{
		cout << m_Name << "拿着 << " << m_Phone.m_PhoneName << " >> 牌手机,玩着 :" << m_Game.m_GameName << endl;
	}

	~Person()
	{
		cout << "Person 的析构函数调用" << endl;
	}
	string m_Name; //姓名
	Phone m_Phone; //手机
	Game  m_Game;  //游戏
};

void test01()
{
	//当其他类对象 作为本类成员,先构造其他类对象,再构造自身,析构的顺序和构造相反
	Person p("张三", "苹果", "王者荣耀");
	p.PlayGame();

	Person p2("李四", "三星", "消消乐");
	p2.PlayGame();
}

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

(10)explicit关键字

explicit用途: 防止利用隐式类型转换方式来构造对象
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class MyString
{
public:
	MyString(char* str)
	{

	}
	//explicit用途: 防止利用隐式类型转换方式来构造对象
	explicit MyString(int len)
	{

	}
};

void test01()
{


	MyString str1(10);

	MyString str2 = MyString(100);

	//MyString str3 = 10; // "10"

}

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(11)new和delete

new和delete
1、malloc 和 new 区别
	1.1、malloc 和 free 属于 库函数     new 和delete属于 运算符
	1.2、malloc不会调用构造函数   new会调用构造函数
	1.3、malloc返回void* C++下要强转     new 返回创建的对象的指针
2、注意事项 不要用void*去接受new出来的对象,利用void*无法调用析构函数
3、利用new创建数组
	3.1、Person * pPerson = new Person[10];
	3.2、释放数组时候  需要加[]
	3.3、delete [] pPerson;
4、堆区开辟数组,一定会调用默认构造函数
5、栈上开辟数组,可不可以没有默认构造,可以没有默认构造
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

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

	Person(int a)
	{
		cout << "Person有参构造调用" << endl;
	}

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

//malloc 和 new 区别
//malloc 和 free 属于 库函数     new 和delete属于 运算符
//malloc不会调用构造函数   new会调用构造函数
//malloc返回void* C++下要强转     new 返回创建的对象的指针

void test01()
{
	Person* p = new Person;

	delete p;
}

//注意事项 不要用void*去接受new出来的对象,利用void*无法调用析构函数
void test02()
{
	void* p = new Person;

	delete p;
}

//利用new开辟数组
void test03()
{
	//int * pInt = new int[10];
	//double * pD = new double[10];

	//堆区开辟数组,一定会调用默认构造函数
	Person* pPerson = new Person[10];

	释放数组时候  需要加[]
	//delete [] pPerson;

	//栈上开辟数组,可不可以没有默认构造,可以没有默认构造
	//Person pArray[10] = { Person(10), Person(20), Person(20) };

}

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

	system("pause");
	return EXIT_SUCCESS;
}

(12)静态成员

1、静态成员变量
	1.1、所有对象都共享同一份数据
	1.2、编译阶段就分配内存
	1.3、类内声明、类外初始化
	1.4、访问方式有两种:通过对象访问、通过类名访问
	1.5、静态成员变量也是有访问权限
2、静态成员函数
	2.1、所有对象都共享同一份函数
	2.2、静态成员函数  只可以访问  静态成员变量,不可以访问非静态成员变量(因为不知道要修改的是哪一个对象)
	2.3、静态成员函数  也是有访问权限的
	2.4、静态成员函数 有两种访问方式:通过对象 、通过类名
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;


class Person
{
public:
	//1、静态成员变量
	//静态成员变量 :编译阶段就分配了内存
	//类内声明 、类外初始化
	//静态成员变量 所有对象都共享同一份数据
	static int m_A;


	//2、静态成员函数
	//所有对象都共享同一个func函数
	static void func()
	{
		//m_C = 100; //静态成员函数 不能访问非静态成员变量
		m_A = 100; //静态成员函数 能访问静态成员变量
		cout << "func调用" << endl;
	}

	int m_C;

private:
	static int m_B; //私有静态成员变量 

	static void func2()
	{

	}
};

int Person::m_A = 0;
int Person::m_B = 0;


void test01()
{
	//1、通过对象进行访问
	Person p1;
	cout << p1.m_A << endl;

	Person p2;
	p2.m_A = 100;

	cout << p1.m_A << endl;

	//2、通过类名进行访问
	cout << Person::m_A << endl;

	//静态成员变量 也是有访问权限的,私有权限类外访问不到
	//cout << Person::m_B << endl;

}

void test02()
{
	//通过对象
	Person p1;
	p1.func();
	//通过类名
	Person::func();
	//Person::func2();  静态成员函数也是有访问权限的
}

int main() {

	//test01();
	test02();
	system("pause");
	return EXIT_SUCCESS;
}

(13)单例模式

单例模式 – 主席类案例
	1、通过一个类 只能实例化唯一的一个对象
	2、私有化
		2.1、默认构造
		2.2、拷贝构造
		2.3、唯一实例指针
	3、对外提供 getInstance 接口,将指针返回

单例模式 – 打印机案例
	1、和主席类案例一样设计单例模式
	2、提供打印功能并且统计打印次数
主席类案例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//主席类
class ChairMan
{
public:
	static ChairMan* getInstacne()
	{
		return singleMan;
	}
private:
	//将构造函数私有化,不可以创建多个对象
	ChairMan() {};

	ChairMan(const ChairMan&) {};

	//public:
private:
	//将主席指针 私有化,对外提供只读接口
	static  ChairMan* singleMan; //类内声明  类外初始化
};

ChairMan* ChairMan::singleMan = new ChairMan;

void test01()
{
	/*ChairMan c1;
	ChairMan c2;
	ChairMan * c3 = new ChairMan;*/

	//ChairMan * c1 = ChairMan::singleMan;
	//ChairMan * c2 = ChairMan::singleMan;


	ChairMan* c1 = ChairMan::getInstacne();
	ChairMan* c2 = ChairMan::getInstacne();

	//ChairMan * c3 = new ChairMan(*c1);

	if (c1 == c2)
	{
		cout << "c1 = c2" << endl;
	}
	else
	{
		cout << "c1 != c2" << endl;
	}

	//if (c1 == c3)
	//{
	//	cout << "c1 = c3" << endl;
	//}
	//else
	//{
	//	cout << "c1 != c3" << endl;
	//}

}

int main() {
	test01();
	system("pause");
	return EXIT_SUCCESS;
}
打印机案例
单例模式 – 打印机案例
1和主席类案例一样设计单例模式
    单例之前打印helloword操作
2提供打印功能并且统计打印次数
    重新创建一个打印机对象之后m_Count是接着的
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>
class Printer
{
public:
	static Printer* getInstance()
	{
		return printer;
	}

	void printText(string text)
	{
		m_Count++;
		cout << text << endl;
	}

	int m_Count;

private:

	Printer()
	{
		m_Count = 0;
		//cout << "打印机构造调用" << endl; 
	};

	Printer(const Printer& p) {};

	static Printer* printer;


};

Printer* Printer::printer = new Printer;


void test01()
{
	Printer* p1 = Printer::getInstance();
	p1->printText("入职证明");
	p1->printText("离职证明");
	p1->printText("加薪申请");
	p1->printText("旅游申请");

	cout << "打印机使用次数: " << p1->m_Count << endl;


	Printer* p2 = Printer::getInstance();
	p2->printText("调休申请");

	cout << "打印机使用次数: " << p1->m_Count << endl;

}

int main() {
	//	cout << "mian函数调用" << endl;

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(14)C++对象模型初探

1、类中的成员变量 和 成员函数  是分开存储的
2、只有非静态成员变量  属于类对象上
3、空类的sizeof结果  1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//#pragma pack(1)

class Person
{
public:

	int m_A; //只有非静态成员变量  属于类对象上

	void func()  //成员函数  并不属于类对象上
	{

	}

	static int m_B; //静态成员变量  不属于类对象上

	static void func2()//静态成员函数  不属于类对象上
	{

	}

	double m_C;

};
int Person::m_B = 0;

void test01()
{
	//空类的sizeof结果是1  原因  每个对象都应该在内存上有独一无二的地址,因此给空对象分配1个字节空间
	// Person pArr[10]  pArr[0]  pArr[1]
	Person p1;
	//  空对象 大小 1  
	cout << "sizeof = " << sizeof(p1) << endl;
}


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

(15)this指针

this指针
1、this指针 指向 被调用的成员函数 所属的对象
2、this指针可以解决名称冲突
3、this指针 隐式加在每个成员函数中
4、*this 就是本体
	p1.personAddPerson(p2).personAddPerson(p2).personAddPerson(p2); //链式编程
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
public:
	Person(int age)
	{
		//用途1 :解决名称冲突
		this->age = age;
	}

	//this指针 隐式加在每个成员函数中
	bool compareAge(Person& p)
	{
		if (this->age == p.age)
		{
			return true;
		}
		return false;
	}

	Person& personAddPerson(Person& p)
	{
		this->age += p.age;
		return *this; //*this 就是本体
	}

	int age;
};

void test01()
{
	//this指针 指向 被调用的成员函数 所属的对象
	Person p1(10);

	cout << "p1的年龄为: " << p1.age << endl;

	Person p2(10);

	bool ret = p1.compareAge(p2);
	if (ret)
	{
		cout << "p1与p2年龄相等" << endl;
	}

	p1.personAddPerson(p2).personAddPerson(p2).personAddPerson(p2); //链式编程
	cout << "p1的年龄为: " << p1.age << endl;

}

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(16)空指针访问成员函数

1、如果成员函数中没有用到this指针,可以用空指针调用成员函数
2、如果成员函数中用到了this,那么这个this需要加判断,防止代码down掉
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
public:

	void showClass()
	{
		cout << "class Name is Person" << endl;
	}

	void showAge()
	{
		/*	if (this == NULL)
			{
			return;
			}*/
			//m_Age = 0;
		cout << "age = " << this->m_Age << endl;
	}

	int m_Age;
};


void test01()
{
	Person* p = NULL;

	//p->showClass();

	p->showAge();

}

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

(17)常对象和常函数

1、常函数
	1.1、成员函数 声明后面加const
	1.2、void showPerson() const
	1.3、const目的是为了修饰成员函数中的this指针,让指针指向的值不可以修改
	1.4、有些属性比较特殊,依然在常函数或者常对象中可以修改,需要加入关键字 mutable
2、常对象
	2.1、const Person p
	2.2、常对象也不许修改成员属性
	2.3、常对象只能调用常函数
3、对于成员函数 ,可不可以 用static 和 const同时修饰 ,不可以
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
public:
	Person(int age)
	{
		this->m_Age = age;
	}

	//常函数 : 修饰成员函数中的 this指针,让指针指向的值不可以修改
	void showPerson() const
	{
		//m_Age = 100;

		m_A = 100;

		//this指针的本质: const Person * const this 
		//this = NULL; 指针的指向不可以修改,而指针指向的值 可以改
		cout << "person age = " << this->m_Age << endl;
	}

	void func()
	{
		m_Age = 100;
		cout << "func调用" << endl;
	}

	int m_Age;

	mutable int m_A; //常函数中或常对象 有些特殊属性依然想修改,加入关键字 mutable
};

void test01()
{
	//常对象
	const Person p1(10);
	//p1.m_Age = 10;
	p1.m_A = 10;

	p1.showPerson();

	//p1.func(); //常对象 只能调用常函数
}

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(18)友元

1、全局函数作为友元函数
	1.1、利用friend关键字让全局函数  goodGay作为本类好朋友,可以访问私有成员
	1.2、friend  void goodGay(Building * buliding);
2、类作为友元类
	2.1、让goodGay类作为 Building的好朋友,可以访问私有成员
	2.2、friend class GoodGay;
3、类中的成员函数作为友元函数
	3.1、//让GoodGay类中的 visit成员函数作为友元
	3.2、friend void GoodGay::visit();
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>

class Building
{
	//利用friend关键字让全局函数  goodGay作为本类好朋友,可以访问私有成员
	friend void goodGay(Building* buliding);

public:
	Building()
	{
		this->m_SittingRoom = "客厅";
		this->m_BedRoom = "卧室";
	}

public:
	string m_SittingRoom; //客厅
private:
	string m_BedRoom; //卧室
};

//好基友全局函数  可以访问Building的私有属性
void goodGay(Building* buliding)
{
	cout << "好基友正在访问:" << buliding->m_SittingRoom << endl;

	cout << "好基友正在访问:" << buliding->m_BedRoom << endl;
}

void test01()
{
	Building buliding;
	goodGay(&buliding);
}

int main() {
	test01();


	system("pause");
	return EXIT_SUCCESS;
}
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>
class Building;
class GoodGay
{
public:

	GoodGay();

	void visit();

	Building* m_building;
};

class Building
{
	//让goodGay类作为 Building的好朋友,可以访问私有成员
	friend class GoodGay;

public:
	Building();

	string m_SittingRoom;

private:
	string m_BedRoom;
};

Building::Building()
{
	this->m_SittingRoom = "客厅";
	this->m_BedRoom = "卧室";
}

GoodGay::GoodGay()
{
	this->m_building = new Building;
}

void GoodGay::visit()
{
	cout << "好基友正在访问: " << this->m_building->m_SittingRoom << endl;
	cout << "好基友正在访问: " << this->m_building->m_BedRoom << endl;
}

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

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>
class Building;
class GoodGay
{
public:

	GoodGay();

	void visit(); //可以访问building的私有

	void visit2(); // 不可以访问building的私有

	Building* m_building;
};

class Building
{
	//让GoodGay类中的 visit成员函数作为友元
	friend void GoodGay::visit();
public:
	Building();

	string m_SittingRoom;

private:
	string m_BedRoom;
};

Building::Building()
{
	this->m_SittingRoom = "客厅";
	this->m_BedRoom = "卧室";
}

GoodGay::GoodGay()
{
	this->m_building = new Building;
}

void GoodGay::visit()
{
	cout << "好基友正在访问: " << this->m_building->m_SittingRoom << endl;
	cout << "好基友正在访问: " << this->m_building->m_BedRoom << endl;
}

void GoodGay::visit2()
{
	cout << "好基友正在访问: " << this->m_building->m_SittingRoom << endl;
	//cout << "好基友正在访问: " << this->m_building->m_BedRoom << endl;
}


void test01()
{
	GoodGay gg;
	gg.visit();
	gg.visit2();
}

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(19)强化训练-数组类封装

强化训练-数组类封装
1、设计类 myArray
2、属性
	2.1、int m_Capacity数组容量
	2.2、int m_Size   数组大小
	2.3、int pAddress 维护真实在堆区创建的数组指针
3、行为
	3.1、默认构造
	3.2、有参构造
	3.3、拷贝构造
	3.4、析构
	3.5、根据位置 设置数据
	3.6、根据位置  获取数据
	3.7、尾插
	3.8、获取数组容量
	3.9、获取数组大小

4.运算符重载篇

(1)加号运算符重载

1、对于内置的数据类型,编译器知道如何进行运算
2、但是对于自定义数据类型,编译器不知道如何运算
3、利用运算符重载 可以让符号有新的含义
4、利用加号重载  实现p1 + p2 Person数据类型相加操作
5、利用成员函数  和  全局函数 都可以实现重载
6、关键字 operator +
7、成员本质  p1.operator+(p2)
8、全局本质  operator+(p1,p2)
9、简化   p1 + p2
10、运算符重载 也可以发生函数重载
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
public:
	Person(){};
	Person(int a, int b) :m_A(a), m_B(b)
	{};

	//利用成员函数实现加号运算符重载
	//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;
};


//利用全局函数实现加号运算符重载
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(10, 10);
	Person p2(20, 20);

	Person p3 = p1 + p2;
	//Person p3 = operator+(p1, p2); //全局函数本质

	//Person p3 = p1.operator+(p2); //成员函数本质

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


	//运算符重载  可不可以发生 函数重载?  可以

	//p1 + 10;

}

int main(){

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(2)左移运算符

左移运算符重载
1 不要滥用运算符重载,除非有需求
2 不能对内置数据类型进行重载 
3 对于自定义数据类型,不可以直接用 cout << 输出
4 需要重载 左移运算符  
5 如果利用成员 函数重载 ,无法实现让cout 在左侧,因此不用成员重载
6 利用全局函数 实现左移运算符重载
	ostream& operator<<(ostream &cout, Person & p1)
7 如果想访问类中私有内存,可以配置友元实现
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
	friend ostream& operator<<(ostream &cout, Person & p1);

public:

	Person(int a, int b)
	{
		this->m_A = a;
		this->m_B = b;
	}

	//试图利用成员函数 做<<重载
	//void operator<<( Person & p)    // p.operator<<(cout)    p<<cout
	//{
	//
	//}

private:
	int m_A;
	int m_B;
};

//利用全局函数 实现左移运算符重载
ostream& operator<<(ostream &cout, Person & p1)
{
	cout << "m_A = " << p1.m_A << " m_B = " << p1.m_B;
	return cout;
}

void test01()
{
	Person p1(10, 10);

	cout << p1 << endl;

}

int main(){

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(3)递增运算符重载

递增运算符重载
1 前置递增
	1.1 MyInter& operator++()
2 后置递增
	2.1 MyInter operator++(int)
3 前置++ 效率高于 后置++ 效率 ,因为后置++会调用拷贝构造,创建新的数据
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class MyInter
{
	friend ostream& operator<<(ostream& cout, MyInter& myInt);
public:
	MyInter()
	{
		m_Num = 0;
	}

	//前置++ 重载
	MyInter& operator++()
	{
		this->m_Num++;
		return *this;
	}

	//后置++ 重载
	MyInter operator++(int)
	{
		//先记录初始状态
		MyInter temp = *this;

		this->m_Num++;

		return temp;
	}

private:
	int m_Num;
};


ostream& operator<<(ostream& cout , MyInter& myInt)
{
	cout << myInt.m_Num;
	return cout;
}

void test01()
{
	MyInter myInt;
	cout << ++(++myInt) << endl; 
	cout << myInt << endl; 
}

void test02()
{
	MyInter myInt;

	cout << myInt++ << endl;
	cout << myInt << endl;

}

int main(){
	//test01();
	test02();
	//int a = 0;
	//cout << ++(++a) << endl;
	//cout << a << endl;

	//int b = 0;
	//cout << (b++)++ << endl;

	system("pause");
	return EXIT_SUCCESS;
}

(4)指针运算符重载

1 智能指针
2 用途: 托管new出来的对象的释放
3 设计smartPoint智能指针类,内部维护 Person * ,在析构时候释放堆区new出来的person对象
4 重载  ->   * 让  sp智能指针用起来向真正的指针
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
public:
	Person(int age)
	{	
		cout << "Person的有参构造调用" << endl;
		this->m_Age = age;
	}

	void showAge()
	{
		cout << "年龄为: "<< this->m_Age << endl;
	}

	~Person()
	{
		cout << "Person的析构调用" << endl;
	}

	int m_Age;
};

class SmartPoint
{
public:
	SmartPoint(Person * person)
	{
		this->m_person = person;
	}

	//重载->运算符
	Person * operator->()
	{
		return this->m_person;
	}
	//重载 * 运算符
	Person& operator*()
	{
		return *m_person;
	}
	~SmartPoint()
	{
		if (this->m_person)
		{
			delete this->m_person;
			this->m_person = NULL;
		}
	}
private:
	Person * m_person;
};

void test01()
{
	//Person * p = new Person(18);
	//(*p).showAge();
	//p->showAge();
	//delete p;


	//利用智能指针 管理 new出来的person的释放操作
	SmartPoint sp(new Person(18));

	sp->showAge(); // 本质sp->->showAge(); 编译器简化为 sp->showAge();

	(*sp).showAge();

}


int main(){
	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(5)赋值运算符重载

赋值运算符重载
1 编译器会默认个一个类添加4个函数
	1.1 默认构造、析构 、  拷贝构造(值拷贝)、 operator=(值拷贝)
2 如果类中有属性创建在堆区,利用编译器提供的 = 赋值运算就会出现 堆区内存重复释放的问题
3 解决方案:利用深拷贝  重载 =运算符
4 Person& operator=( const Person &p)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//编译器 默认给一个类4个函数   默认构造   析构   拷贝构造 (值拷贝)  operator= (值拷贝)
class Person
{
public:
	Person(char * name, int age)
	{
		this->m_Name = new char[strlen(name) + 1];
		strcpy(this->m_Name, name);
		this->m_Age = age;
	}

	//重载 =
	Person& operator=( const Person &p)
	{
		//先判断原来堆区释放有内容,如果有先释放
		if (this->m_Name != NULL)
		{
			delete [] this->m_Name;
			this->m_Name = NULL;
		}
		//深拷贝
		this->m_Name = new char[strlen(p.m_Name) + 1];
		strcpy(this->m_Name, p.m_Name);
		this->m_Age = p.m_Age;
		return *this;
	}

	//拷贝构造
	Person(const Person & p)
	{
		this->m_Name = new char[strlen(p.m_Name) + 1];
		strcpy(this->m_Name, p.m_Name);
		this->m_Age = p.m_Age;
	}

	~Person()
	{
		if (this->m_Name!=NULL)
		{
			delete [] this->m_Name;
			this->m_Name = NULL;
		}
	}

	char * m_Name;
	int m_Age;
};


void test01()
{
	Person p1("Tom",10);
	
	Person p2("Jerry",19);
	p2 = p1;

	Person p3("", 0);
	p3 = p2 = p1;


	Person p4 = p3;

	cout << "p1姓名: "<< p1.m_Name << "  p1年龄: " << p1.m_Age << endl;
	cout << "p2姓名: "<< p2.m_Name << "  p2年龄: " << p2.m_Age << endl;
	cout << "p3姓名: " << p3.m_Name << " p3年龄: " << p3.m_Age << endl;

}

int main(){

	test01();

	/*int a = 10;
	int b = 20;
	int c;
	c = a = b;
	cout << "a = " << a << " b = " << b << " c = " << c << endl;*/


	system("pause");
	return EXIT_SUCCESS;
}

(6)[]运算符重载

[]运算符重载
1 int& operator[](int index);
2 实现访问数组时候利用[] 访问元素

(7)关系运算符重载

1 对于自定义数据类型,编译器不知道如果进行比较
2 重载  ==   !=号
3 bool operator==( Person & p)
4 bool operator!=(Person & p)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class  Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	bool operator==(Person& p)
	{
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
		{
			return true;
		}
		return false;
	}


	bool operator!=(Person& p)
	{
		return !(this->m_Name == p.m_Name && this->m_Age == p.m_Age);
	}


	string m_Name;
	int m_Age;
};

void test01()
{
	//int a = 10;
	//int b = 20;
	//if (a == b)
	//{
	//	cout << "a == b " << endl;
	//}
	//else
	//{
	//	cout << "a != b " << endl;
	//}

	Person p1("Tom", 18);

	Person p2("Tom", 19);

	if (p1 == p2)
	{
		cout << "p1 == p2 " << endl;
	}
	else
	{
		cout << "a != b " << endl;
	}


	if (p1 != p2)
	{
		cout << "a != b " << endl;
	}
	else
	{
		cout << "p1 == p2 " << endl;
	}

}


int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(8)函数调用运算符重载

1 重载 ()
2 使用时候很像函数调用,因此称为仿函数
3 void operator()(string text)
4 int operator()(int a,int b)
5 仿函数写法不固定,比较灵活
6	cout << MyAdd()(1, 1) << endl; // 匿名函数对象 特点:当前行执行完立即释放
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>

class MyPrint
{
public:
	void operator()(string text)
	{
		cout << text << endl;
	}
};

void MyPrint2(string str)
{
	cout << str << endl;
}

void test01()
{
	MyPrint myPrint;
	myPrint("hello world"); // 仿函数  本质是一个对象   函数对象

	MyPrint2("hello world"); //普通函数
}

class MyAdd
{
public:
	int operator()(int a, int b)
	{
		return a + b;
	}
};

void test02()
{
	MyAdd myAdd;
	cout << myAdd(1, 1) << endl;
	cout << MyAdd()(1, 1) << endl; // 匿名函数对象 特点:当前行执行完立即释放
}

int main() {

	//test01();
	test02();
	system("pause");
	return EXIT_SUCCESS;
}

(9)不要重载 && 和 ||

1 原因是无法实现短路特性
2 建议:将<< 和 >>写成全局函数,其他可重载的符号写到成员即可

5.继承篇

(1)继承基本语法

1 继承优点:减少重复的代码,提高代码复用性
2// 语法: class 子类 : 继承方式   父类
3// News		子类    派生类
4// BasePage 父类    基类
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//class News
//{
//public:
//
//	void header()
//	{
//		cout << "公共的头部" << endl;
//	}
//	void footer()
//	{
//		cout << "公共的底部" << endl;
//	}
//	void leftList()
//	{
//		cout << "公共的左侧列表" << endl;
//	}
//
//	void content()
//	{
//		cout << "新闻播报..." << endl;
//	}
//};
//
//
//class Sport
//{
//public:
//
//	void header()
//	{
//		cout << "公共的头部" << endl;
//	}
//	void footer()
//	{
//		cout << "公共的底部" << endl;
//	}
//	void leftList()
//	{
//		cout << "公共的左侧列表" << endl;
//	}
//
//	void content()
//	{
//		cout << "世界杯赛况..." << endl;
//	}
//};


//利用继承模拟网页
//继承优点: 减少重复的代码,提高代码复用性
class BasePage
{
public:
	void header()
	{
		cout << "公共的头部" << endl;
	}
	void footer()
	{
		cout << "公共的底部" << endl;
	}
	void leftList()
	{
		cout << "公共的左侧列表" << endl;
	}
};
// 语法: class 子类 : 继承方式   父类
// News		子类    派生类
// BasePage 父类    基类
class News : public BasePage
{
public:
	void content()
	{
		cout << "新闻播报..." << endl;
	}
};

class Sport :public BasePage
{
public:
	void content()
	{
		cout << "世界杯..." << endl;
	}

};


void test01()
{
	News news;
	cout << "新闻页面内容如下:" << endl;
	news.header();
	news.footer();
	news.leftList();
	news.content();


	Sport sp;
	cout << "体育页面内容如下:" << endl;
	sp.header();
	sp.footer();
	sp.leftList();
	sp.content();

}


int main() {
	test01();


	system("pause");
	return EXIT_SUCCESS;
}

(2)继承方式

1 公共继承
 1.1父类中公共权限,子类中变为公共权限
 1.2父类中保护权限,子类中变为保护权限
 1.3父类中私有权限,子类访问不到
2 保护继承
 2.1父类中公共权限,子类中变为保护权限
 2.2父类中保护权限,子类中变为保护权限
 2.3父类中私有权限,子类访问不到
3 私有继承
 3.1父类中公共权限,子类中变为私有权限
 3.2父类中保护权限,子类中变为私有权限
 3.3父类中私有权限,子类访问不到
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

/   公共继承  
class Base1
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son1 :public Base1
{
public:

	void func()
	{
		m_A = 100; //父类中 公共权限 子类中变为  公共权限
		m_B = 100; //父类中 保护权限 子类中变为  保护权限
		//m_C = 100;// 父类中私有成员,子类无法访问
	}
};

void test01()
{
	Son1 s1;
	s1.m_A = 100; //在Son1中 m_A是公共权限  类外可以访问
	//s1.m_B = 100; //在Son1中 m_B是保护权限  类外不可以访问
}

/   保护继承  
class Base2
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son2 : protected Base2
{
public:
	void func()
	{
		m_A = 100;//父类中 公共权限 子类中变为  保护权限
		m_B = 100;//父类中 保护权限 子类中变为  保护权限
		//m_C = 100;//父类中私有成员,子类无法访问
	}
};

void test01()
{
	Son2 s;
	//s.m_A = 100;  //子类中 保护权限 无法访问
	//s.m_B = 100;  //子类中 保护权限 无法访问
	//s.m_C = 100; //子类本身没有访问权限
}

/   私有继承  
class Base3
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};
class Son3 :private Base3
{
public:
	void func()
	{
		m_A = 100;  //父类中 公共权限 子类中变为  私有权限
		m_B = 100;  //父类中 保护权限 子类中变为  私有权限
		//m_C = 100;//父类中私有成员,子类无法访问
	}
};
class GrandSon3 :public Son3
{
public:
	void func()
	{
		//m_A = 100;//在Son3中 已经变为私有权限,GrandSon3访问不到
		//m_B = 100;
	}
};
void test03()
{
	Son3 s;
	//s.m_A = 100;//在Son3中变为私有权限,类外访问不到
	//s.m_B = 100;//在Son3中变为私有权限,类外访问不到
}

int main() {
	system("pause");
	return EXIT_SUCCESS;
}

(3)继承中的对象模型

1 父类中的私有属性,子类是继承下去了,只不过由编译器给隐藏了,访问不到
2 可以利用开发人员工具查看对象模型
3 C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts
4 打开开发人员命令工具
5 跳转盘符 E:
6 跳转文件路径  cd到文件路径下
7 cl /d1 reportSingleClassLayout类名  文件名
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Base
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;  //父类中私有属性,子类访问不到,是由编译器给隐藏了
};

class Son : public Base
{
public:
	int m_D;
};

void test01()
{
	//4  8    12   16
	cout << "size of  Son = " << sizeof(Son) << endl; // 结果为16
}

int main(){
	test01();
	system("pause");
	return EXIT_SUCCESS;
}
ps:推荐
https://coolshell.cn/articles/12176.html

(4)继承中的构造和析构

1 先调用父类构造,再调用其他成员构造, 再调用自身构造 ,析构的顺序与构造相反
2 利用初始化列表语法  显示调用父类中的其他构造函数
3 父类中 构造、析构、拷贝构造 、operator=  是不会被子类继承下去的
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

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

	}

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

};

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

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

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

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

	Other other;
};

void test01()
{
	Son1 s; //先调用父类构造,再调用其他成员构造, 再调用自身构造 ,析构的顺序与构造相反

}

class Base2
{
public:
	Base2(int a)
	{
		this->m_A = a;
		cout << "Base2的构造函数调用" << endl;
	}
	int m_A;
};
class Son2 :public Base2
{
public:
	Son2(int a = 1000) :Base2(a)  //利用初始化列表语法  显示调用父类中的其他构造函数
	{
		cout << "Son2的构造函数调用" << endl;
	}
};
void test02()
{
	Son2 s;
	cout << s.m_A << endl;
}

//父类中 构造、析构、拷贝构造 、operator=  是不会被子类继承下去的

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

	system("pause");
	return EXIT_SUCCESS;
}

(5)继承中的同名成员处理

1 我们可以利用作用域 访问父类中的同名成员
2 当子类重新定义了父类中的同名成员函数,子类的成员函数会 隐藏掉父类中所有重载版本的同名成员,可以利用作用域显示指定调用
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Base
{
public:
	Base()
	{
		this->m_A = 10;
	}

	void func()
	{
		cout << "Base中的func调用" << endl;
	}

	void func(int a)
	{
		cout << "Base中的func(int)调用" << endl;
	}

	int m_A;
};

class Son :public Base
{
public:

	Son()
	{
		this->m_A = 20;
	}

	void func()
	{
		cout << "Son中的func调用" << endl;
	}

	int m_A;
};

void test01()
{
	Son s1;

	cout << "s1.m_A = " <<  s1.m_A << endl;

	//我们可以利用作用域 访问父类中的同名成员
	cout << "Base中的m_A = " << s1.Base::m_A << endl;
}

void test02()
{
	Son s1;
	s1.func();
	s1.Base::func(10);

	//当子类重新定义了父类中的同名成员函数,子类的成员函数会 隐藏掉父类中所有重载版本的同名成员,可以利用作用域显示指定调用

}

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

	system("pause");
	return EXIT_SUCCESS;
}

(6)继承中的同名 静态成员处理

1 结论和 非静态成员 一致
2 只不过调用方式有两种
	2.1 通过对象
	2.2 通过类名
		2.2.1 通过类名的方式 访问 父类作用域下的m_A静态成员变量
		2.2.2 Son::Base::m_A
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Base
{
public:

	static void func()
	{
		cout << "Base中的func调用 " << endl;
	}

	static void func(int a)
	{
		cout << "Base中的func(int a)调用 " << endl;
	}

	static int m_A;

};

int Base::m_A = 10;

class Son :public Base
{
public:

	static void func()
	{
		cout << "Son中的func调用 " << endl;
	}

	static int m_A;
};
int Son::m_A = 20;

void test01()
{
	//1、通过对象访问
	Son s;
	cout << "m_A = " << s.m_A << endl;
	cout << "Base中的m_A = " << s.Base::m_A << endl;

	//2、通过类名访问

	cout << "m_A = " << Son::m_A << endl;
	//通过类名的方式 访问 父类作用域下的m_A静态成员变量
	cout << "Base中的m_A = " << Son::Base::m_A << endl;
}

void test02()
{
	//1、通过对象
	Son s;
	s.func();
	s.Base::func();

	//2、通过类名
	Son::func();
	//当子类重定义父类中同名成员函数,子类的成员函数会隐藏掉父类中所有版本,需要加作用域调用
	Son::Base::func(1);
}

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

	system("pause");
	return EXIT_SUCCESS;
}

(7)多继承基本语法

1  class 子类 : 继承方式  父类1 , 继承方式 父类2 
2  当多继承的两个父类中有同名成员,需要加作用域区分
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Base1
{
public:
	Base1()
	{
		this->m_A = 10;
	}
	int m_A;
};

class Base2
{
public:
	Base2()
	{
		this->m_A = 20;
	}
	int m_A;
};

//多继承
class Son : public Base1, public Base2
{
public:

	int m_C;
	int m_D;
};

void test01()
{
	cout << "sizeof Son = " << sizeof(Son) << endl;

	Son s;
	//当多继承的两个父类中有同名成员,需要加作用域区分
	cout << s.Base1::m_A << endl;
	cout << s.Base2::m_A << endl;
}

int main(){
	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(8)菱形继承

1 两个类有公共的父类  和共同的子类 ,发生菱形继承
2 菱形继承导致数据有两份,浪费资源
3 解决方案:利用虚继承可以解决菱形继承问题
3 1class Sheep : virtual public Animal{};
4//当发生虚继承后,sheep和tuo类中 继承了一个  vbptr指针   虚基类指针   
	指向的是一个 虚基类表  vbtable
5 //虚基类表中记录了  偏移量 ,通过偏移量 可以找到唯一的一个m_Age
6 利用地址偏移找到 vbtable中的偏移量 并且访问数据
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//动物类
class Animal
{
public:
	int m_Age; // 年龄
};

//Animal称为 虚基类

//羊类
class Sheep : virtual public Animal{};
//驼类
class Tuo : virtual public Animal{};

//羊驼
class SheepTuo : public Sheep, public Tuo
{
};


void test01()
{
	SheepTuo st;

	st.Sheep::m_Age = 10;
	st.Tuo::m_Age = 20;

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

	//当发生虚继承后,sheep和tuo类中 继承了一个  vbptr指针   虚基类指针   指向的是一个 虚基类表  vbtable
	//虚基类表中记录了  偏移量 ,通过偏移量 可以找到唯一的一个m_Age
}


void test02()
{
	SheepTuo st;
	st.m_Age = 10;

	//通过Sheep找到 偏移量
	//*(int *)&st 解引用到了 虚基类表中
	cout << *((int *)*(int *)&st + 1) << endl;

	//通过Tuo 找到偏移量
	cout << *((int *)*((int *)&st + 1) + 1) << endl;

	//通过偏移量  访问m_Age

	cout << "m_Age = " << ((Animal *)((char *)&st + *((int *)*(int *)&st + 1)))->m_Age << endl;

	cout << "m_Age = " << *((int *)((char *)&st + *((int *)*(int *)&st + 1))) << endl;
}

int main(){

	//test01();
	test02();
	system("pause");
	return EXIT_SUCCESS;
}

6.多态篇

(1)静态联编动态联编

1 静态多态和动态多态
2 静态多态:函数重载,运算符重载
3 动态多态:
	3.1 先有继承关系
	3.2 父类中有虚函数,子类重写父类中的虚函数
	3.3 父类的指针或引用  指向子类的对象
4 静态多态在编译阶段绑定地址,地址早绑定,静态联编
5 动态多次在运行阶段绑定地址,地址晚绑定,动态联编

(2) 多态原理

1、当父类写了虚函数后,类内部结构发生改变,多了一个vfptr
2、vfptr 虚函数表指针  ---- > vftable 虚函数表
3、虚函数表内部记录着 虚函数的入口地址
4、当父类指针或引用指向子类对象,发生多态,调用是时候从虚函数中找函数入口地址
5、虚函数 关键字  virtual
6、利用指针的偏移调用 函数 
	6.1、((void(*)()) (*(int *)*(int *)animal)) ();
	6.2、typedef void( __stdcall *FUNPOINT)(int);
	6.3、(FUNPOINT (*((int*)*(int*)animal + 1)))(10);
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

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

	virtual void eat(int a)
	{
		cout << "动物在吃饭" << endl;
	}
};

class Cat :public Animal
{
public:
	void speak()
	{
		cout << "小猫在说话" << endl;
	}

	void eat(int a)
	{
		cout << "小猫在吃饭" << endl;
	}
};

class Dog :public Animal
{
public:
	void speak()
	{
		cout << "小狗在说话" << endl;
	}
};


//动态多态产生条件:
//先有继承关系
//父类中有虚函数,子类重写父类中的虚函数
//父类的指针或引用  指向子类的对象

//对于有父子关系的两个类  指针或者引用 是可以直接转换的
void doSpeak(Animal& animal) //Animal & animal = cat;
{
	//如果地址早就绑定好了,地址早绑定,属于静态联编
	//如果想调用小猫说话,这个时候函数的地址就不能早就绑定好,而是在运行阶段再去绑定函数地址,属于地址晚绑定,叫动态联编
	animal.speak();

}

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

	Dog dog;
	doSpeak(dog);
}

void test02()
{
	//cout << "sizeof  Animal = " << sizeof (Animal) << endl;

	Animal* animal = new Cat;
	//animal->speak();
	// *(int *)animal 解引用到虚函数表中
	// *(int *)*(int *)animal 解引用到函数speak地址

	//调用猫说话
	((void(*)()) (*(int*)*(int*)animal)) ();


	//C/C++默认调用惯例  __cdecl
	//用下列调用时候 真实调用惯例  是 __stdcall
	//调用猫吃饭
	typedef void(__stdcall* FUNPOINT)(int);

	(FUNPOINT(*((int*)*(int*)animal + 1)))(10);
}

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

	system("pause");
	return EXIT_SUCCESS;
}

(3)多态案例 - 计算器案例

1 设计抽象计算器类,分别实现加减乘计算,继承于抽象计算器类,重写虚函数
2 利用多态可以调用不同计算器
3 多态的好处
	3.1代码可读性强
	3.2组织结构清晰
	3.3扩展性强
4 开闭原则: 对扩展进行开放  对修改进行关闭
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>

//class calculator
//{
//public:
//
//	int getResult( string oper)
//	{
//		if (oper == "+")
//		{
//			return m_A + m_B;
//		}
//		else if (oper == "-")
//		{
//			return m_A - m_B;
//		}
//		else if (oper == "*")
//		{
//			return m_A * m_B;
//		}
//		
//	}
//
//	int m_A;
//	int m_B;
//};

//设计原则 : 开闭原则
// 对扩展进行开放  对修改进行关闭

//利用多态实现计算器
class AbstractCalculator
{
public:

	//纯虚函数
	//如果一个类中包含了纯虚函数,那么这个类就无法实例化对象了,这个类通常我们称为 抽象类
	//抽象类的子类 必须要重写 父类中的纯虚函数,否则也属于抽象类
	virtual int getResult() = 0;

	//virtual int getResult()
	//{
	//	return 0;
	//}

	int m_A;
	int m_B;
};
//加法计算器
class AddCalculator :public AbstractCalculator
{
public:
	virtual int getResult()
	{
		return m_A + m_B;
	}
};

//减法计算器
class SubCalculator :public AbstractCalculator
{
public:
	virtual int getResult()
	{
		return m_A - m_B;
	}
};

//乘法计算器
class MulCalculator :public AbstractCalculator
{
public:
	virtual int getResult()
	{
		return m_A * m_B;
	}
};

class Test :public AbstractCalculator
{
	int getResult(){ return 0; };
};

void test01()
{
	//calculator c;
	//c.m_A = 10;
	//c.m_B = 10;
	//cout << c.getResult("+") << endl;


	AbstractCalculator * calculator = new AddCalculator;

	calculator->m_A = 100;
	calculator->m_B = 200;

	cout << calculator->getResult() << endl;

	delete calculator;

	calculator = new SubCalculator;
	calculator->m_A = 100;
	calculator->m_B = 200;

	cout << calculator->getResult() << endl;

}

int main(){

	//test01();

	//AbstractCalculator abc;   抽象类是无法实例化对象的
	//Test t; //如果不重写父类的纯虚函数 ,也无法实例化对象

	system("pause");
	return EXIT_SUCCESS;
}

(4)纯虚函数和抽象

1 语法: virtual int getResult() = 0;
2 如果一个类中包含了纯虚函数,那么这个类就无法实例化对象了,这个类通常我们称为 抽象类
3 抽象类的子类 必须要重写 父类中的纯虚函数,否则也属于抽象类

(5)虚析构和纯虚析构

1 虚析构语法:
	1.1 virtual ~Animal(){}
	1.2 如果子类中有指向堆区的属性,那么要利用虚析构技术 在delete的时候 调用子类的析构函数
2 纯虚析构语法:
	2.1 virtual ~Animal() = 0;
	2.2 Animal::~Animal(){ .. }
	2.3	纯虚析构 需要有声明(类内) 也需要有实现(类外)
	2.4	如果一个类中 有了 纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Animal
{
public:
	Animal()
	{
		cout << "Animal的构造函数调用" << endl;
	}
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
	//如果子类中有指向堆区的属性,那么要利用虚析构技术 在delete的时候 调用子类的析构函数
	//virtual ~Animal()
	//{
	//	cout << "Animal的析构函数调用" << endl;
	//}

	//纯虚析构 需要有声明 也需要有实现
	//如果一个类中 有了 纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了
	virtual ~Animal() = 0;

};

Animal::~Animal()
{
	cout << "Animal的纯虚析构函数调用" << endl;
}


class Cat :public Animal
{
public:
	Cat(char* name)
	{
		cout << "Cat的构造函数调用" << endl;
		this->m_Name = new char[strlen(name) + 1];
		strcpy(this->m_Name, name);
	}

	virtual void speak()
	{
		cout << this->m_Name << " 小猫在说话" << endl;
	}

	~Cat()
	{
		if (this->m_Name)
		{
			cout << "Cat的析构函数调用" << endl;
			delete[] this->m_Name;
			this->m_Name = NULL;
		}
	}
	char* m_Name;
};

void test01()
{
	char name[] = "zky";
	Animal* animal = new Cat(name);
	animal->speak();

	delete animal;
}

int main() {
	test01();

	//Animal aaa;  在Animal中有了纯虚析构,也属于抽象类

	system("pause");
	return EXIT_SUCCESS;
}

(6)向上类型转换和向下类型转换

1 父转子   向下类型转换   不安全
2 子转父   向上类型转换   安全
3 如果发生多态,那么转换永远都是安全的

(7) 重载、重写、重定义

1 重载
	1.1 函数重载
	1.2 同一个作用域下,函数名称相同,参数个数、顺序、类型不同
	1.3 和函数返回值,没有关系 
	1.4 const 也可以作为重载条件 
		do(const Teacher& t){} 
		do(Teacher& t)
2 重写
	2.1 子类重写父类中的虚函数,函数返回值、函数名、形参列表完全一致称为重写
3 重定义
	3.1 子类重新定义父类中的同名成员函数,隐藏掉父类中同名成员函数,如果想调用加作用域

(8) 多态案例2 - 电脑组装案例

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//CPU基类
class CPU
{
public:
	virtual void calculate() = 0;
};
//显卡基类
class VideoCard
{
public:
	virtual void display() = 0;
};
//内存基类
class Memory
{
public:
	virtual void storage() = 0;
};

//电脑类
class computer
{
public:

	computer(CPU* cpu, VideoCard* vc, Memory* mem)
	{
		cout << "电脑构造调用" << endl;
		this->m_Cpu = cpu;
		this->m_Vc = vc;
		this->m_Mem = mem;
	}

	void doWork()
	{
		this->m_Cpu->calculate();
		this->m_Vc->display();
		this->m_Mem->storage();
	}

	~computer()
	{
		cout << "电脑析构调用" << endl;
		if (this->m_Cpu)
		{
			delete this->m_Cpu;
			this->m_Cpu = NULL;
		}
		if (this->m_Vc)
		{
			delete this->m_Vc;
			this->m_Vc = NULL;
		}
		if (this->m_Mem)
		{
			delete this->m_Mem;
			this->m_Mem = NULL;
		}
	}

	CPU* m_Cpu;
	VideoCard* m_Vc;
	Memory* m_Mem;
};
 
//inter厂商
class intelCPU :public CPU
{
public:
	void calculate()
	{
		cout << "intelCPU开始计算了" << endl;
	}
};

class intelVideoCard :public VideoCard
{
public:
	void display()
	{
		cout << "intel 显卡开始显示了" << endl;
	}
};
class intelMemory :public Memory
{
public:
	void storage()
	{
		cout << "intel 内存条开始存储了" << endl;
	}
};

//Lenovo 厂商
class LenovoCPU :public CPU
{
public:
	void calculate()
	{
		cout << "Lenovo CPU开始计算了" << endl;
	}
};

class LenovoVideoCard :public VideoCard
{
public:
	void display()
	{
		cout << "Lenovo 显卡开始显示了" << endl;
	}

};
class LenovoMemory :public Memory
{
public:
	void storage()
	{
		cout << "Lenovo 内存条开始存储了" << endl;
	}
};

void test01()
{
	cout << "第一台电脑组成:" << endl;

	CPU* intelCpu = new intelCPU;
	VideoCard* lenovoVC = new LenovoVideoCard;
	Memory* lenovoMem = new LenovoMemory;

	computer c1(intelCpu, lenovoVC, lenovoMem);

	c1.doWork();

	cout << "第二台电脑组成:" << endl;

	CPU* intelCpu2 = new LenovoCPU;
	VideoCard* lenovoVC2 = new intelVideoCard;
	Memory* lenovoMem2 = new intelMemory;

	computer c2(intelCpu2, lenovoVC2, lenovoMem2);

	c2.doWork();
}

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

7.函数模板

(1)函数模板

1、泛型编程 – 模板技术 特点:类型参数化
2、template< typename T >  告诉编译器后面紧跟着的函数或者类中出现T,不要报错,T是一个通用的数据类型
3、实现通用两个数进行交换函数
4、使用
	4.1、自动类型推导   必须要推导出一致的T才可以使用
	4.2、显示指定类型   mySwap<int>(a,b);
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

void mySwapInt(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}

void mySwapDouble(double& a, double& b)
{
	double temp = a;
	a = b;
	b = temp;
}

//利用模板实现通用交换函数
template<typename T>  //T代表一个通用的数据类型,告诉编译器如果下面紧跟着的函数或者类中出现T不要报错
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

//模板不能单独使用,必须指定出T才可以使用
template<typename T>
void mySwap2()
{
}

void test01()
{
	int a = 10;
	int b = 20;
	//	mySwapInt(a, b);
	char c1 = 'c';
	//1、自动类型推导,必须推导出一致的T数据类型,才可以正常的使用模板
	//mySwap(a, c1); //推导不出一致的T,因此无法调用
	//mySwap(a, b);

	//2、显示指定类型
	mySwap<int>(a, b);

	mySwap2<int>(); //必须告诉编译器T类型才可以调用

	cout << a << endl;
	cout << b << endl;


	double c = 3.14;
	double d = 1.1;

	//	mySwapDouble(c, d);
	mySwap(c, d);

	cout << c << endl;
	cout << d << endl;

}

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(2) 实现对char和 int类型数组进行排序

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

template <class T>
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

//需求 通过一个通用排序函数  实现对char和int数组的排序,排序顺序 从大到小, 算法  选择排序
template< class  T > // typename 和 class 一样
void mySort(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		int max = i;
		for (int j = i + 1; j < len; j++)
		{
			if (arr[max] < arr[j])
			{
				max = j;
			}
		}

		//判断 算出的max和开始认定的i是否一致,如果不同交换数据
		if (i != max)
		{
			mySwap(arr[i], arr[max]);
		}
	}

}

template<class T>
void printArray(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << endl;
	}
}

void test01()
{
	char charArray[] = "helloworld";

	int len = strlen(charArray);
	mySort(charArray, len);

	//	printArray(charArray, len);


	int intArray[] = { 5, 7, 1, 4, 2, 3 };
	len = sizeof(intArray) / sizeof(int);
	mySort(intArray, len);
	printArray(intArray, len);


}

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(3) 函数模板和普通函数的区别以及调用规则

1、区别
	1.1、如果使用自动类型推导,是不可以发生隐式类型转换的
	1.2、普通函数 可以发生隐式类型转换
2、调用规则
	2.1、如果函数模板和普通函数都可以调用,那么优先调用普通函数
	2.2、如果想强制调用函数模板,可以使用空模板参数列表
		2.2.1、myPrint<>(a, b);
	2.3、函数模板也可以发生函数重载
	2.4、如果函数模板能产生更好的匹配,那么优先使用函数模板
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//1、函数模板和普通函数的区别
template<class T>
T myAdd(T a , T b)
{
	return a + b;
}

int myAdd2(int a, int b)
{
	return a + b;
}
void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';
	//myAdd(a, c); //如果使用自动类型推导,是不可以发生隐式类型转换的

	cout << myAdd2(a, c) << endl; //普通函数 可以发生隐式类型转换
}


//2、函数模板和普通函数的调用规则
template<class T>
void myPrint(T a ,T b)
{
	cout << "函数模板调用" << endl;
}

/*
函数模板通过具体类型产生不同的函数  --- 通过函数模板产生的函数  称为 模板函数
void myPrint(int a ,int b)
{
cout << "函数模板调用" << endl;
}

void myPrint(double a ,double b)
{
cout << "函数模板调用" << endl;
}
*/

template<class T>
void myPrint(T a, T b ,T c)
{
	cout << "函数模板(T a, T b ,T c)调用" << endl;
}

void myPrint(int a, int b)
{
	cout << "普通函数调用" << endl;
}



void test02()
{
	//1、如果函数模板和普通函数都可以调用,那么优先调用普通函数
	int a = 10;
	int b = 20;
	//myPrint(a, b);

	//2、如果想强制调用函数模板,可以使用空模板参数列表
	myPrint<>(a, b);

	//3、函数模板也可以发生函数重载
	myPrint(a, b, 10);

	//4、如果函数模板能产生更好的匹配,那么优先使用函数模板
	char c = 'c';
	char d = 'd';
	myPrint(c, d);

}

int main(){

	//test01();

	test02();

	system("pause");
	return EXIT_SUCCESS;
}

(4)模板的实现机制和模板局限性

模板的实现机制
1、编译器并不是把函数模板处理成能够处理任何类型的函数
2、函数模板通过具体类型产生不同的函数 ---  通过函数模板产生的函数 称为模板函数
3、编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
模板局限性
1、模板并不是真实的通用,对于自定义数据类型,可以使用具体化技术,实现对自定义数据类型特殊使用
2、template<> bool myCompare(Person &a, Person &b)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;

};

//显示两个变量 对比 函数
template<class T>
bool myCompare(T& a, T& b)
{
	if (a == b)
	{
		return true;
	}
	return false;
}

//利用具体化技术 实现对自定义数据类型 提供特殊模板
template<> bool myCompare(Person& a, Person& b)
{
	if (a.m_Name == b.m_Name && a.m_Age == b.m_Age)
	{
		return true;
	}
	return false;
}

void test01()
{
	//int a = 10;
	//int b = 10;

	//bool ret = myCompare(a, b);

	//if (ret )
	//{
	//	cout << "a == b" << endl;
	//}
	//else
	//{
	//	cout << "a != b" << endl;
	//}

	Person p1("Tom", 19);
	Person p2("Tom", 20);

	bool ret = myCompare(p1, p2);
	if (ret)
	{
		cout << "p1 == p2" << endl;
	}
	else
	{
		cout << "p1 != p2" << endl;
	}
}

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(5)类模板

类模板和函数模板区别:
1 类模板不可以使用自动类型推导,只能用显示指定类型
2 类模板中 可以有默认参数
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>
template<class NAMETYPE, class AGETYPE = int > //类模板中 可以有默认参数
class Person
{
public:
	Person(NAMETYPE name, AGETYPE age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	void showPerson()
	{
		cout << "姓名: " << this->m_Name << " 年龄: " << this->m_Age << endl;
	}
	NAMETYPE m_Name;
	AGETYPE m_Age;
};

void test01()
{
	//类模板和函数模板区别:
	//1、类模板不可以使用自动类型推导,只能用显示指定类型
	//2、类模板中 可以有默认参数
	//Person p1("孙悟空", 100);

	Person<string > p1("孙悟空", 100);
	p1.showPerson();
}

int main(){

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(6)类模板中成员函数创建时机

类模板中的成员函数  并不是一开始创建的,而是在运行阶段确定出T的数据类型才去创建
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1 show 调用" << endl;
	}
};

class Person2
{
public:
	void showPerson2()
	{
		cout << "Person2 show 调用" << endl;
	}
};


//类模板中的成员函数  并不是一开始创建的,而是在运行阶段确定出T的数据类型才去创建
template<class T>
class MyClass
{
public:
	void func1()
	{
		obj.showPerson1();
	}
	void func2()
	{
		obj.showPerson2();
	}

	T obj;
};

void test01()
{
	MyClass <Person2> p1;
	//p1.func1();
	p1.func2();
}

int main(){

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(7)类模板做函数参数

1、指定传入类型
	void doWork(Person <string, int>&p)
2、参数模板化
	2.1、template<class T1, class T2>
	2.2、void doWork2(Person <T1, T2>&p)
3、整个类 模板化
	3.1、template<class T>
	3.2、void doWork3( T &p)
4、查看T数据类型
	typeid(T).name()
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>
template<class T1, class T2> //类模板中 可以有默认参数
class Person
{
public:
	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	void showPerson()
	{
		cout << "姓名: " << this->m_Name << " 年龄: " << this->m_Age << endl;
	}
	T1 m_Name;
	T2 m_Age;
};

//1、指定传入类型
void doWork(Person <string, int>&p)
{
	p.showPerson();
}

void test01()
{
	Person <string, int>p("孙悟空", 999);
	doWork(p);
}


//2、参数模板化
template<class T1, class T2>
void doWork2(Person <T1, T2>&p)
{
	cout << "T1数据类型: " << typeid(T1).name() << endl;
	cout << "T2数据类型: " << typeid(T2).name() << endl;
	p.showPerson();
}

void test02()
{
	Person<string, int>p("猪八戒", 998);
	doWork2(p);
}


//3、整个类 模板化
template<class T>
void doWork3( T &p)
{
	cout << "T的数据类型: " << typeid(T).name() << endl;
	p.showPerson();
}

void test03()
{
	Person<string, int>p("唐僧", 10000);
	doWork3(p);
}

int main(){

	//test01();
	//test02();
	test03();

	system("pause");
	return EXIT_SUCCESS;
}

(8)类模板碰到继承的问题以及解决

1 必须要指定出父类中的T数据类型,才能给子类分配内存
2 template<class T1 ,class T2>
3 class Son2 :public Base2<T2>
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

template<class T>
class Base
{
public:
	T m_A;
};

//必须要指定出父类中的T数据类型,才能给子类分配内存
class Son :public Base<int>
{

};



template<class T>
class Base2
{
public:
	T m_A;
};

template<class T1 ,class T2>
class Son2 :public Base2<T2>
{
public:

	Son2()
	{
		cout << typeid(T1).name() << endl;
		cout << typeid(T2).name() << endl;
	}

	T1 m_B;
};

void test01()
{
	Son2 <int, double> s;

}

int main(){
	
	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(9)类模板中的成员函数类外实现

void Person<T1, T2>::showPerson()
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>

template<class T1, class T2>
class Person
{
public:
	Person(T1 name, T2 age);
	//{
	//	this->m_Name = name;
	//	this->m_Age = age;
	//}

	void showPerson();
	//{
	//	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
	//}

	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
	this->m_Name = name;
	this->m_Age = age;
}

template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}


void test01()
{
	Person <string, int>p("Tom", 10);
	p.showPerson();
}

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(10)类模板的分文件编写问题以及解决

1 类模板中的成员函数,不会一开始创建,因此导致分文件编写时连接不到函数的实现,出现无法解析的外部命令错误
2 解决方式1:
	2.1 直接包含.cpp文件 (不推荐)
3 解决方式2:
	3.1 将类声明和实现写到同一个文件中,将文件的后缀名改为 .hpp 即可
person.hpp
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

template<class T1, class T2>
class Person
{
public:
	Person(T1 name, T2 age);
	
	void showPerson();
	
	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
	this->m_Name = name;
	this->m_Age = age;
}

template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <string>
#include "person.hpp"
using namespace std;

void test01()
{
	Person<string, int>p("zky", 19);
	p.showPerson();
}

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

(11)类模板碰到友元的问题以及解决

1友元类内实现
	1.1	friend void printPerson(Person<T1, T2> &p)
2友元类外实现
	2.1 声明: 
		friend void printPerson2<>(Person<T1, T2> &p);
	2.2 实现:	
		2.2.1 template<class T1,class T2>
		2.2.2 void printPerson2(Person<T1, T2> &p){ 。。。}
		
			template<class T1,class T2>
			class Person;

			template<class T1,class T2>
			void printPerson2(Person<T1, T2> &p);
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>

template<class T1, class T2>
class Person;

template<class T1, class T2>
void printPerson2(Person<T1, T2>& p);

template<class T1, class T2>
void printPerson3(Person<T1, T2>& p)
{
	cout << "类外实现 --- 姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}

template<class T1, class T2>
class Person
{
	//1、友元函数 类内实现
	friend void printPerson(Person<T1, T2>& p)
	{
		cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
	}

	//2、友元函数 类外实现
	friend void printPerson2<>(Person<T1, T2>& p);


	friend void printPerson3<>(Person<T1, T2>& p);
public:
	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

private:
	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
	cout << "类外实现 --- 姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}

void test01()
{
	Person <string, int> p("Tom", 100);
	printPerson(p);
	printPerson2(p);
	printPerson3(p);
}

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

8.C++类型转化

(1) 静态类型转换 static_cast

1允许内置数据类型转换
2允许父子之间的指针或者引用的转换
3语法 static_cast<目标类型>(原变量/原对象)

(2)动态类型转换 dynamic_cast

1 不允许内置数据类型转换 
2 允许父子之间指针或者引用的转换
	2.1 父转子  不安全的  转换失败
	2.2 子转父  安全   转换成功
	2.3 如果发生多态,总是安全,可以成功
3 语法 dynamic_cast<目标类型>(原变量/原对象)

(3)常量转换 const_cast

1 只允许 指针或者引用 之间转换
2 语法 const	_cast<目标类型>(原变量/原对象)

(4)重新解释转换

reinterpret_cast 最不安全一种转换,不建议使用
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//1、静态类型转换  static_cast
void test01()
{
	//允许内置数据类型之间转换
	char a = 'a';

	double d = static_cast<double>(a);

	cout << d << endl;

}

class Base { virtual void func() {} };
class Son :public Base { virtual void func() {} };
class Other {};

void test02()
{
	Base* base = NULL;
	Son* son = NULL;

	//语法:   static_cast<目标类型>(原对象)
	//父子之间的指针或者引用可以转换
	//将base 转为 Son *   父转子  向下类型转换  不安全
	Son* son2 = static_cast<Son*> (base);

	//son  转为 Base*    子转父  向上类型转换  安全
	Base* base2 = static_cast<Base*>(son);


	//base 转为Other*
	//Other * other = static_cast<Other*>(base);  转换无效

}


//动态类型转换  dynamic_cast
void test03()
{
	//不允许内置数据类型之间转换
	//char c = 'c';
	//double d = dynamic_cast<double>(c);
}

void test04()
{
	Base* base = new Son;
	Son* son = NULL;

	//将base 转为 Son *  父转子 不安全  如果发生了多态,那么转换总是安全的
	Son* son2 = dynamic_cast<Son*>(base);


	//son 转为 Base*  子转父  安全
	Base* base2 = dynamic_cast<Base*>(son);

	//base 转 Other*
	//Other* other = dynamic_cast<Other*>(base); //无法转换
}

//常量转换  const_cast
void test05()
{
	//不可以将非指针或非引用做const_cast转换
	const int* p = NULL;
	int* pp = const_cast<int*>(p);

	const int* ppp = const_cast<const int*>(pp);


	//const int a = 10;
	//int b = const_cast<int>(a);


	int num = 10;
	int& numRef = num;

	const int& num2 = const_cast<const int&>(numRef);

}

//重新解释转换  reinterpret_cast 最不安全一种转换,不建议使用
void test06()
{
	int a = 10;
	int* p = reinterpret_cast<int*>(a);

	Base* base = NULL;
	//base 转 Other *
	Other* other = reinterpret_cast<Other*>(base);
}

int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

9.异常

(1) 异常的基本语法

1 C++异常的处理关键字
	try  throw  catch
2 可以出现异常的代码 放到 try块
3 利用throw抛出异常
4 利用catch捕获异常
5 catch( 类型) 如果想捕获其他类型 catch(…)
6 如果捕获到的异常不想处理,而继续向上抛出,利用 throw
7 异常必须有函数进行处理,如果都不去处理,程序自动调用 terminate函数,中断掉
8 异常可以是自定义数据类型

(2)栈解旋

1 从try代码块开始,到throw抛出异常之前,所有栈上的数据都会被释放掉,
2 释放的顺序和创建顺序相反的,这个过程我们称为栈解旋
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>

class MyException
{
public:
	void printError()
	{
		cout << "我自己的异常" << endl;
	}
};

class Person
{
public:
	Person()
	{
		cout << "Person的默认构造函数调用" << endl;
	}
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}
};

int myDivision(int a , int b)
{
	if ( b == 0)
	{
		//return -1;
		//throw 1; //抛出int类型的异常
		//throw 'a'; //抛出char类型的异常
		//throw 3.14; //抛出double类型的异常
		/*string str = "abc";
		throw str;*/


		//从try代码块开始,到throw抛出异常之前,所有栈上的数据都会被释放掉,
		//释放的顺序和创建顺序相反的,这个过程我们称为栈解旋
		Person p1;
		Person p2;


		throw MyException(); //抛出 MyException的匿名对象
	}

	return a / b;
}

void test01()
{
	int a = 10;
	int b = 0;

	//C语言处理异常 有缺陷:返回值不统一,返回值只有一个,无法区分是结果还是异常
	//int ret =myDivision(a, b);
	//if ( ret == -1)
	//{
	//	cout << "异常" << endl;
	//}

	try
	{
		myDivision(a, b);
	}
	catch (int)
	{
		cout << "int类型异常捕获" << endl;
	}
	catch (char)
	{
		cout << "char类型异常捕获" << endl;
	}
	catch (double)
	{
		//捕获到了异常,但是不想处理,继续向上抛出这个异常
		//异常必须有函数进行处理,如果没有任何处理,程序自动调用 terminate 函数,让程序中断
		throw;
		cout << "double类型异常捕获" << endl;
	}
	catch (MyException e)
	{
		e.printError();
	}
	catch (...)
	{
		cout << "其他类型异常捕获" << endl;
	}
}

int main(){
	try
	{
		test01();
	}
	catch (double)
	{
		cout << "double函数中 double类型异常捕获" << endl;
	}
	catch (...)
	{
		cout << "main函数中 其他类型异常捕获" << endl;
	}
	system("pause");
	return EXIT_SUCCESS;
}

(3)异常的接口声明

1 在函数中 如果限定抛出异常的类型,可以用异常的接口声明
2 语法: void func()throw(int ,double)
3 throw(空)代表 不允许抛出异常
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>

//异常接口声明   throw(空)代表 不允许抛出异常
void func()throw(int ,double)
{

    throw 3.14;
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();


    try
    {
        func();
    }
    catch(int)
    {
        qDebug() << "int 类型的异常捕获"<<endl;
    }
    catch(...)
    {
         qDebug() << "其他 类型的异常捕获"<<endl;
    }

    return a.exec();
}

(4)异常变量的生命周期

1	抛出的是 throw MyException();   catch (MyException e) 调用拷贝构造函数 效率低
2	抛出的是 throw MyException();   catch (MyException &e)  只调用默认构造函数 效率高 推荐
3	抛出的是 throw &MyException();  catch (MyException *e) 对象会提前释放掉,不能在非法操作
4	抛出的是 new MyException();     catch (MyException *e) 只调用默认构造函数 自己要管理释放
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class MyException
{
public:
	MyException()
	{
		cout << "MyException默认构造函数调用" << endl;
	}

	MyException(const MyException & e)
	{
		cout << "MyException拷贝构造函数调用" << endl;
	}

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

};

void doWork()
{
	throw  new MyException();
}

void test01()
{
	try
	{
		doWork();
	}
	//抛出的是 throw MyException();  catch (MyException e) 调用拷贝构造函数 效率低
	//抛出的是 throw MyException();  catch (MyException &e)  只调用默认构造函数 效率高 推荐
	//抛出的是 throw &MyException(); catch (MyException *e) 对象会提前释放掉,不能在非法操作
	//抛出的是 new MyException();   catch (MyException *e) 只调用默认构造函数 自己要管理释放
	catch (MyException *e)
	{
		cout << "自定义异常捕获" << endl;
		delete e;
	}

}

int main(){
	test01();


	system("pause");
	return EXIT_SUCCESS;
}

(5)异常的多态使用

1 提供基类异常类
	1.1 class BaseException
	1.2 纯虚函数  virtual void printError() = 0;
2 子类空指针异常 和  越界异常 继承 BaseException
3 重写virtual void printError()
4 测试 利用父类引用指向子类对象
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//异常的基类
class BaseException
{
public:
	virtual void printError() = 0;
};

//空指针异常
class NULLPointerException :public BaseException
{
public:
	virtual void printError()
	{
		cout << "空指针异常" << endl;
	}
};

//越界异常
class OutOfRangeException :public BaseException
{
public:
	virtual void printError()
	{
		cout << "越界异常" << endl;
	}

};

void doWork()
{
	//throw NULLPointerException();
	throw OutOfRangeException();
}

void test01()
{
	try
	{
		doWork();
	}
	catch (BaseException & e)
	{
		e.printError();
	}
}

int main(){

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(6)系统标准异常

1 引入头文件  #include <stdexcept>
2 抛出越界异常 throw out_of_range(“…”)
3 获取错误信息  catch( exception & e )     e.what();
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <stdexcept> //  std 标准  except 异常

class Person
{
public:
	Person(int age)
	{
		if (age < 0 || age > 150)
		{
			throw out_of_range("年龄必须在 0 ~ 150之间");
			//throw length_error("年龄必须在 0 ~ 150之间");
		}
		else
		{
			this->m_Age = age;
		}
	}

	int m_Age;
};

void test01()
{
	try
	{
		Person p(151);
	}
	//catch ( out_of_range &e)
	catch ( exception &e)
	{
		cout << e.what() << endl;
	}

}

int main(){
	test01();

	system("pause");
	return EXIT_SUCCESS;
}

(7)编写自己的异常类

1、编写myOutofRange 继承 Exception类
2、重写 virtual const char *  what() const
3、将sting 转为 const char * 
	3.1 .c_str()
4、const char * 可以隐式类型转换为 string  反之不可以
5、测试,利用多态打印出错误提示信息
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class MyOutOfRangeException:public exception
{
public:

	MyOutOfRangeException( const char * str)
	{
		//const char * 可以隐式类型转换为 string  反之不可以
		this->m_errorInfo = str;
	}

	MyOutOfRangeException(string str)
	{
		this->m_errorInfo = str;
	}

	virtual const char *  what() const
	{
		//将string 转为 const char * 
		return m_errorInfo.c_str();
	}

	string m_errorInfo;
};

class Person
{
public:
	Person(int age)
	{
		if (age < 0 || age > 150)
		{
			throw MyOutOfRangeException( string( "年龄必须在0到150之间"));
		}
		else
		{
			this->m_Age = age;
		}
	}

	int m_Age;
};


void test01()
{
	try
	{
		Person p(1000);
	}
	catch (exception & e)
	{
		cout << e.what() << endl;
	}


}

int main(){

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

10.输入输出流

(1)标准输入流

1、cin.get() 获取一个字符
2、cin.get(两个参数) 获取字符串
	2.1、利用cin.get获取字符串时候,换行符遗留在缓冲区中
3、cin.getline() 获取字符串
	3.1、利用cin.getline获取字符串时候,换行符不会被取走,也不在缓冲区中,而是直接扔掉
4、cin.ignore() 忽略 默认忽略1个字符, 如果填入参数X,代表忽略X个字符
5、cin.peek()  偷窥  
6、cin.putback()  放回 放回原位置
7、案例1
	7.1、判断用户输入的内容 是字符串还是数字
8、案例2
	8.1、用户输入 0 ~ 10 之间的数字,如果输入有误,重新输入
	8.2、标志位  cin.fail()   0代表正常  1代表异常
	8.3、cin.clear()  cin.sync() 清空缓冲区 重置标志位
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

/*
cin.get() //一次只能读取一个字符
cin.get(一个参数) //读一个字符
cin.get(两个参数) //可以读字符串
cin.getline()
cin.ignore()
cin.peek()
cin.putback()
*/
void test01()
{
	//输入  a  s     第一次 a  第二次  s  第三次 换行  第四次 等待下次输入
	char c = cin.get();

	cout << "c = " << c << endl;

	c = cin.get();

	cout << "c = " << c << endl;

	c = cin.get();

	cout << "c = " << c << endl;


	c = cin.get();

	cout << "c = " << c << endl;
}


void test02()
{
	char buf[1024] = { 0 };
	cin.get(buf, 1024);

	char c = cin.get();
	//利用cin.get获取字符串时候,换行符遗留在缓冲区中
	if (c == '\n')
	{
		cout << "换行符遗留在缓冲区" << endl;
	}
	else
	{
		cout << "换行符不在缓冲区" << endl;
	}
	cout << buf << endl;

}

void test03()
{
	char buf[1024] = { 0 };

	//利用cin.getline获取字符串时候,换行符不会被取走,也不在缓冲区中,而是直接扔掉
	cin.getline(buf, 1024);

	char c = cin.get();
	if (c == '\n')
	{
		cout << "换行符遗留在缓冲区" << endl;
	}
	else
	{
		cout << "换行符不在缓冲区" << endl;
	}
	cout << buf << endl;

}

//cin.ignore() 忽略 默认忽略1个字符, 如果填入参数X,代表忽略X个字符
void test04()
{
	cin.ignore(2);
	char c = cin.get();
	cout << "c = " << c << endl;
}

//cin.peek() 偷窥
void test05()
{
	char c = cin.peek();
	cout << "c = " << c << endl;

	c = cin.get();
	cout << "c = " << c << endl;

	c = cin.get();
	cout << "c = " << c << endl;

}

//cin.putback() 放回
void test06()
{
	char c = cin.get();
	cin.putback(c);

	char buf[1024] = { 0 };

	cin.getline(buf, 1024);
	cout << buf << endl;
}

//案例1、 判断用户输入的内容 是字符串还是数字  12345     abcde
void test07()
{
	cout << "请输入一个字符串或者数字" << endl;
	char c = cin.peek();

	if (c >= '0' && c <= '9')
	{
		int num;
		cin >> num;
		cout << "您输入的是数字 为:" << num << endl;
	}
	else
	{
		char buf[1024] = { 0 };
		cin >> buf;
		cout << "您输入的是字符串 :" << buf << endl;
	}
}

//案例2、 用户输入 0 ~ 10 之间的数字,如果输入有误,重新输入
void test08()
{
	cout << "请输入 0 ~ 10 之间的数字" << endl;

	int num;

	while (true)
	{
		cin >> num;

		if (num >= 0 && num <= 10)
		{
			cout << "输入正确,输入值为:" << num << endl;
			break;
		}

		//清空缓冲区 重置标志位 
		cin.clear();
		cin.sync();
		//cin.ignore();//vs2013以上版本加入 
		//如果标志位为 0  代表缓冲区正常    如果标志位为1   缓冲区异常
		//cout << " cin.fail()  = " << cin.fail() << endl;

		cout << "输入有误,请重新输入: " << endl;
	}

}

int main() {
	//test01();
	//test02();
	//test03();
	//test04();
	//test05();
	//test06();
	//test07();
	test08();

	system("pause");
	return EXIT_SUCCESS;
}

(2)标准输出流

1、cout.put() //向缓冲区写字符
2、cout.write() //从buffer中写num个字节到当前输出流中。
3、通过 流成员函数 格式化输出
	3.1、	int number = 99;
	3.2、	cout.width(20); //指定宽度为20
	3.3、	cout.fill('*'); //填充
	3.4、	cout.setf(ios::left);  //左对齐
	3.5、	cout.unsetf(ios::dec); //卸载十进制
	3.6、	cout.setf(ios::hex);  //安装十六进制
	3.7、	cout.setf(ios::showbase);  //显示基数
	3.8、	cout.unsetf(ios::hex);  //卸载十六进制
	3.9、	cout.setf(ios::oct);   //安装八进制
	3.10、	cout << number << endl;

(3)通过控制符 格式化输出

int number = 99;
cout << setw(20)     //设置宽度
	<< setfill('~')  //设置填充
	<< setiosflags(ios::showbase)  //显示基数
	<< setiosflags(ios::left)  //设置左对齐
	<< hex   //显示十六进制
	<< number
	<< endl;

引入头文件  #include< iomanip>
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <iomanip> //控制符格式化输出  头文件

/*
cout.put() //向缓冲区写字符
cout.write() //从buffer中写num个字节到当前输出流中。
*/
void test01()
{
	//cout.put('h').put('e');

	char buf[] = "hello world";
	cout.write(buf, strlen(buf));

	cout << "hello world" << endl;

}

//1、通过 流成员函数 格式化输出
void test02()
{
	int number = 99;
	cout.width(20); //指定宽度为20
	cout.fill('*'); //填充
	cout.setf(ios::left);  //左对齐
	cout.unsetf(ios::dec); //卸载十进制
	cout.setf(ios::hex);  //安装十六进制
	cout.setf(ios::showbase);  //显示基数
	cout.unsetf(ios::hex);  //卸载十六进制
	cout.setf(ios::oct);   //安装八进制
	cout << number << endl;
}

//2、使用控制符 格式化输出
void test03()
{
	int number = 99;
	cout << setw(20)     //设置宽度
		<< setfill('~')  //设置填充
		<< setiosflags(ios::showbase)  //显示基数
		<< setiosflags(ios::left)  //设置左对齐
		<< hex   //显示十六进制
		<< number
		<< endl;
}

int main(){

	//test01();
	//test02();
	test03();

	system("pause");
	return EXIT_SUCCESS;
}

(4)文件读写

1、头文件  #inlcude < fstream>
2、写文件  
	2.1、 ofstream  ofs (文件路径,打开方式  ios::out )
	2.2、判断文件是否打开成功  ofs.is_open
	2.3、ofs << “…”
	2.4、关闭文件  ofs.close();
3读文件
	3.1、ifstream  ifs(文件路径,打开方式  ios::in)
	3.2、判断文件是否打开成功  ofs.is_open
	3.3、利用4种方式 对文件进行读取
	3.4、关闭文件  ifs.close();
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <fstream> //文件流
#include <string>

void test01()
{
	//写文件  o --  输出    
	ofstream  ofs("./test.txt", ios::out | ios::trunc);

	//ofs.open("./test.txt", ios::out | ios::trunc);  设置打开方式 以及路径

	if (!ofs.is_open())
	{
		cout << "文件打开失败" << endl;
		return;
	}

	ofs << "姓名:孙悟空" << endl;
	ofs << "年龄:999" << endl;
	ofs << "性别:男" << endl;

	//关闭文件
	ofs.close();

}


void test02()
{
	//读文件   i  -- 输入
	ifstream  ifs;
	ifs.open("./test.txt", ios::in);

	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 )
	{
		cout << c;
	}

	ifs.close();
}

int main(){

	//test01();
	test02();

	system("pause");
	return EXIT_SUCCESS;
}
  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值