一、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 标准输入输出流
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
#include "game1.h"
#include "game2.h"
using namespace std;
void test01() {
Gotest02::gotest();
}
namespace A
{
int m_A;
void func();
struct Person
{};
class Animal
{};
}
void test02()
{
}
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;
}
namespace B
{
int m_B = 100;
}
void test04()
{
cout << "B空间下的m_A = " << B::m_A << endl;
cout << "B空间下的m_B = " << B::m_B << endl;
}
namespace
{
int m_C = 1000;
int m_D = 2000;
}
void test05()
{
cout << "m_C = " << m_C << endl;
cout << "m_D = " << ::m_D << endl;
}
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
using namespace std;
namespace KingGlory
{
int SunWuKongId = 1;
}
namespace LOL
{
int SunWuKongId = 3;
}
void test01()
{
using KingGlory::SunWuKongId;
cout << SunWuKongId << endl;
}
void test02()
{
int SunWuKongId = 2;
using namespace KingGlory;
using namespace LOL;
cout << KingGlory::SunWuKongId << endl;
cout << LOL::SunWuKongId << endl;
}
int main()
{
test01();
test02();
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
#include
#include
#include
#include
#include
int a;
int a = 10;
int getRectS(int w, int h)
{
}
void test01()
{
printf("%d\n", getRectS(10, 10, 10));
}
void test02()
{
char* p = malloc(64);
}
struct Person
{
int age;
};
void test03()
{
struct Person p;
p.age = 100;
}
void test04()
{
int a = 10;
int b = 20;
printf("ret = %d\n", a > b ? a : b);
*(a > b ? &a : &b) = 100;
printf("a = %d\n", a);
printf("b = %d\n", b);
}
const int m_A = 100;
void test05()
{
const int m_B = 100;
int* p = &m_B;
*p = 200;
printf("%d\n", m_B);
}
int main(void)
{
test01();
test02();
test03();
test04();
test05();
system("pause");
return EXIT_SUCCESS;
}
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
int a;
int getRectS(int w, int h)
{
return w * h;
}
void test01()
{
printf("%d\n",getRectS(10,10));
}
void test02()
{
char* p = (char*)malloc(64);
}
struct Person
{
int age;
void func()
{
age++;
}
};
void test03()
{
Person p;
p.age = 17;
p.func();
cout << "p的age = " << p.age << endl;
}
bool flag = true;
void test04()
{
cout << sizeof(bool) << endl;
cout << flag << endl;
}
void test05()
{
int a = 10;
int b = 20;
printf("ret = %d\n", a > b ? a : b);
(a < b ? a : b) = 100;
printf("a = %d\n", a);
printf("b = %d\n", b);
}
const int m_A = 100;
void test06()
{
const int m_B = 100;
int* p = (int*)&m_B;
*p = 200;
cout << "m_B = " << m_B << endl;
int arr[m_B];
}
int main()
{
test01();
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
using namespace std;
void test01()
{
const int a = 10;
int* p = (int*)&a;
}
void test02()
{
int a = 10;
const int b = a;
int* p = (int*)&b;
*p = 1000;
cout << "b = " << b << endl;
}
struct Person
{
string m_Name;
int m_Age;
};
void test03()
{
const Person p;
Person* pp = (Person*)&p;
(*pp).m_Name = "Tom";
pp->m_Age = 10;
cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}
int main() {
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
using namespace std;
void test01() {
int a = 10;
int& b = a;
b = 100;
cout << "a= " << a << endl;
cout << "b = " << b << endl;
}
void test02() {
int a = 10;
int& b = a;
int c = 100;
b = c;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
void test03() {
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;
typedef int(ARRAY_TYPE)[10];
ARRAY_TYPE& pArr2 = arr;
for (int i = 0; i < 10; i++)
{
cout << pArr2[i] << endl;
}
}
int main()
{
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
using namespace std;
void mySwap01(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
void mySwap02(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void mySwap03(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
mySwap03(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
int& func()
{
int a = 10;
return a;
}
void test02()
{
int& ref = func();
cout << "ref = " << ref << endl;
cout << "ref = " << ref << endl;
}
int& func2()
{
static int a = 10;
return a;
}
void test03()
{
int& ref = func2();
cout << "ref = " << ref << endl;
cout << "ref = " << ref << endl;
cout << "ref = " << ref << endl;
cout << "ref = " << ref << endl;
func2() = 1000;
cout << "ref = " << ref << endl;
}
int main() {
test01();
test02();
test03();
system("pause");
return EXIT_SUCCESS;
}
(8)指针的引用
指针的引用
1、利用引用可以简化指针
2、可以直接用同级指针的 引用 给同级指针分配空间
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
struct Person
{
int age;
};
void allocateSpace(Person** p)
{
*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)
{
pp = (Person*)malloc(sizeof(Person));
pp->age = 20;
}
void test02()
{
Person* p = NULL;
allocateSpace2(p);
cout << "p.age = " << p->age << endl;
}
int main() {
test01();
test02();
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
using namespace std;
void test01() {
const int& ref = 10;
int* p = (int *)&ref;
*p = 10000;
cout << ref << endl;
}
void showValue(const int& a)
{
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
using namespace std;
#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;
cout << "MYADD1="<<ans<< endl;
}
#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);
}
inline void func();
inline void func() {};
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
using namespace std;
int func(int a, int b = 10, int c=10)
{
return a + b + c;
}
void test01()
{
cout << func(20, 20) << endl;
}
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
using namespace std;
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;
}
void test01()
{
func(1, 3.14);
}
void myFunc(int& a)
{
cout << "myfunc(int &a )调用" << endl;
}
void myFunc(const int& a)
{
cout << "myfunc( const int &a )调用" << endl;
}
void test02()
{
int a = 10;
}
void func2(int a, int b = 10)
{
}
void func2(int a)
{
}
void test03()
{
}
int main() {
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
extern "C" {
#endif
#include
void show();
#ifdef __cplusplus
}
#endif
test.c
#include "test.h"
void show()
{
printf("hello\n");
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include
#include "test.h"
using namespace std;
void test01()
{
show();
}
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
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);
}
};
void test01()
{
struct Person p;
strcpy(p.name, "老王");
p.PersonEat();
}
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 = "李四";
}
int main() {
test01();
system("pause");
return EXIT_SUCCESS;
}
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
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;
string m_Lover;
};
void test01()
{
Person p;
p.setName("张三");
cout << "姓名: " << p.getName() << endl;
p.setAge(100);
cout << "年龄: " << p.getAge() << endl;
p.setLover("苍井");
(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
using namespace std;
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)
{
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;
cout << "c1体积为: " << c1.calculateV() << endl;
Cube c2;
c2.setL(10);
c2.setW(10);
c2.setH(10);
bool ret = compareCube(c1, c2);
if (ret)
{
cout << "c1 与 c2 相等" << endl;
}
else
{
cout << "c1 与 c2 不相等" << endl;
}
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
using namespace std;
class Person
{
public:
Person()
{
cout << "Person的构造函数调用" << endl;
}
~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
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 p5 = 10;
Person p6 = p5;
}
int main() {
test01();
system("pause");
return EXIT_SUCCESS;
}
(5)拷贝构造函数的调用时机
拷贝构造函数的调用时机
1、用已经创建好的对象来初始化新的对象
2、值传递的方式 给函数参数传值
3、以值方式 返回局部对象
#define _CRT_SECURE_NO_WARNINGS
#include
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 p1(18);
Person p2 = Person(p1);
cout << "p2的年龄:" << p2.m_Age<< endl;
}
void doWork(Person p)
{
}
void test02()
{
Person p1(100);
doWork(p1);
}
Person doWork2()
{
Person p;
return p;
}
void test03()
{
Person p = doWork2();
}
int main(){
test03();
system("pause");
return EXIT_SUCCESS;
}
(6)构造函数的调用规则
构造函数的调用规则
1、编译器会给一个类 至少添加3个函数 默认构造(空实现) 析构函数(空实现) 拷贝构造(值拷贝)
2、如果我们自己提供了 有参构造函数,编译器就不会提供默认构造函数,但是依然会提供拷贝构造函数
3、如果我们自己提供了 拷贝构造函数,编译器就不会提供其他构造函数
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
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
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
using namespace std;
class Person
{
public:
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
using namespace std;
#include
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
using namespace std;
class MyString
{
public:
MyString(char* str)
{
}
explicit MyString(int len)
{
}
};
void test01()
{
MyString str1(10);
MyString str2 = MyString(100);
}
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
using namespace std;
class Person
{
public:
Person()
{
cout << "Person构造函数调用" << endl;
}
Person(int a)
{
cout << "Person有参构造调用" << endl;
}
~Person()
{
cout << "Person析构函数调用" << endl;
}
};
void test01()
{
Person* p = new Person;
delete p;
}
void test02()
{
void* p = new Person;
delete p;
}
void test03()
{
Person* pPerson = new Person[10];
}
int main() {
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
using namespace std;
class Person
{
public:
static int m_A;
static void func()
{
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()
{
Person p1;
cout << p1.m_A << endl;
Person p2;
p2.m_A = 100;
cout << p1.m_A << endl;
cout << Person::m_A << endl;
}
void test02()
{
Person p1;
p1.func();
Person::func();
}
int main() {
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
using namespace std;
class ChairMan
{
public:
static ChairMan* getInstacne()
{
return singleMan;
}
private:
ChairMan() {};
ChairMan(const ChairMan&) {};
private:
static ChairMan* singleMan;
};
ChairMan* ChairMan::singleMan = new ChairMan;
void test01()
{
ChairMan* c1 = ChairMan::getInstacne();
ChairMan* c2 = ChairMan::getInstacne();
if (c1 == c2)
{
cout << "c1 = c2" << endl;
}
else
{
cout << "c1 != c2" << endl;
}
}
int main() {
test01();
system("pause");
return EXIT_SUCCESS;
}
打印机案例
单例模式 – 打印机案例
1和主席类案例一样设计单例模式
单例之前打印helloword操作
2提供打印功能并且统计打印次数
重新创建一个打印机对象之后m_Count是接着的
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
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;
};
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() {
test01();
system("pause");
return EXIT_SUCCESS;
}
(14)C++对象模型初探
1、类中的成员变量 和 成员函数 是分开存储的
2、只有非静态成员变量 属于类对象上
3、空类的sizeof结果 1
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
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()
{
Person p1;
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
using namespace std;
class Person
{
public:
Person(int age)
{
this->age = age;
}
bool compareAge(Person& p)
{
if (this->age == p.age)
{
return true;
}
return false;
}
Person& personAddPerson(Person& p)
{
this->age += p.age;
return *this;
}
int age;
};
void test01()
{
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
using namespace std;
class Person
{
public:
void showClass()
{
cout << "class Name is Person" << endl;
}
void showAge()
{
cout << "age = " << this->m_Age << endl;
}
int m_Age;
};
void test01()
{
Person* p = NULL;
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
using namespace std;
class Person
{
public:
Person(int age)
{
this->m_Age = age;
}
void showPerson() const
{
m_A = 100;
cout << "person age = " << this->m_Age << endl;
}
void func()
{
m_Age = 100;
cout << "func调用" << endl;
}
int m_Age;
mutable int m_A;
};
void test01()
{
const Person p1(10);
p1.m_A = 10;
p1.showPerson();
}
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
using namespace std;
#include
class Building
{
friend void goodGay(Building* buliding);
public:
Building()
{
this->m_SittingRoom = "客厅";
this->m_BedRoom = "卧室";
}
public:
string m_SittingRoom;
private:
string m_BedRoom;
};
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
using namespace std;
#include
class Building;
class GoodGay
{
public:
GoodGay();
void visit();
Building* m_building;
};
class 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
using namespace std;
#include
class Building;
class GoodGay
{
public:
GoodGay();
void visit();
void visit2();
Building* m_building;
};
class Building
{
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;
}
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
using namespace std;
class Person
{
public:
Person(){};
Person(int a, int b) :m_A(a), m_B(b)
{};
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;
cout << "p3.m_A = " << p3.m_A << " p3.m_B = " << p3.m_B << endl;
}
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
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;
}
(3)递增运算符重载
递增运算符重载
1 前置递增
1.1 MyInter& operator++()
2 后置递增
2.1 MyInter operator++(int)
3 前置++ 效率高于 后置++ 效率 ,因为后置++会调用拷贝构造,创建新的数据
#define _CRT_SECURE_NO_WARNINGS
#include
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(){
test02();
system("pause");
return EXIT_SUCCESS;
}
(4)指针运算符重载
1 智能指针
2 用途: 托管new出来的对象的释放
3 设计smartPoint智能指针类,内部维护 Person * ,在析构时候释放堆区new出来的person对象
4 重载 -> * 让 sp智能指针用起来向真正的指针
#define _CRT_SECURE_NO_WARNINGS
#include
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()
{
SmartPoint sp(new Person(18));
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
using namespace std;
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();
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
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()
{
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
using namespace std;
#include
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() {
test02();
system("pause");
return EXIT_SUCCESS;
}
(9)不要重载 && 和 ||
1 原因是无法实现短路特性
2 建议:将<< 和 >>写成全局函数,其他可重载的符号写到成员即可
5.继承篇
(1)继承基本语法
1 继承优点:减少重复的代码,提高代码复用性
2// 语法: class 子类 : 继承方式 父类
3// News 子类 派生类
4// BasePage 父类 基类
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
class BasePage
{
public:
void header()
{
cout << "公共的头部" << endl;
}
void footer()
{
cout << "公共的底部" << endl;
}
void leftList()
{
cout << "公共的左侧列表" << endl;
}
};
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
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;
}
};
void test01()
{
Son1 s1;
s1.m_A = 100;
}
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;
}
};
void test01()
{
Son2 s;
}
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;
}
};
class GrandSon3 :public Son3
{
public:
void func()
{
}
};
void test03()
{
Son3 s;
}
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
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()
{
cout << "size of Son = " << sizeof(Son) << endl;
}
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
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;
}
int main() {
test02();
system("pause");
return EXIT_SUCCESS;
}
(5)继承中的同名成员处理
1 我们可以利用作用域 访问父类中的同名成员
2 当子类重新定义了父类中的同名成员函数,子类的成员函数会 隐藏掉父类中所有重载版本的同名成员,可以利用作用域显示指定调用
#define _CRT_SECURE_NO_WARNINGS
#include
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(){
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
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()
{
Son s;
cout << "m_A = " << s.m_A << endl;
cout << "Base中的m_A = " << s.Base::m_A << endl;
cout << "m_A = " << Son::m_A << endl;
cout << "Base中的m_A = " << Son::Base::m_A << endl;
}
void test02()
{
Son s;
s.func();
s.Base::func();
Son::func();
Son::Base::func(1);
}
int main() {
test02();
system("pause");
return EXIT_SUCCESS;
}
(7)多继承基本语法
1 class 子类 : 继承方式 父类1 , 继承方式 父类2
2 当多继承的两个父类中有同名成员,需要加作用域区分
#define _CRT_SECURE_NO_WARNINGS
#include
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
using namespace std;
class Animal
{
public:
int m_Age;
};
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;
}
void test02()
{
SheepTuo st;
st.m_Age = 10;
cout << *((int *)*(int *)&st + 1) << endl;
cout << *((int *)*((int *)&st + 1) + 1) << endl;
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(){
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
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.speak();
}
void test01()
{
Cat cat;
doSpeak(cat);
Dog dog;
doSpeak(dog);
}
void test02()
{
Animal* animal = new Cat;
((void(*)()) (*(int*)*(int*)animal)) ();
typedef void(__stdcall* FUNPOINT)(int);
(FUNPOINT(*((int*)*(int*)animal + 1)))(10);
}
int main() {
test02();
system("pause");
return EXIT_SUCCESS;
}
(3)多态案例 - 计算器案例
1 设计抽象计算器类,分别实现加减乘计算,继承于抽象计算器类,重写虚函数
2 利用多态可以调用不同计算器
3 多态的好处
3.1代码可读性强
3.2组织结构清晰
3.3扩展性强
4 开闭原则: 对扩展进行开放 对修改进行关闭
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
class AbstractCalculator
{
public:
virtual int getResult() = 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()
{
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(){
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
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal的构造函数调用" << endl;
}
virtual void speak()
{
cout << "动物在说话" << 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();
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
using namespace std;
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;
};
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;
}
};
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(a,b);
#define _CRT_SECURE_NO_WARNINGS
#include
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>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
template<typename T>
void mySwap2()
{
}
void test01()
{
int a = 10;
int b = 20;
char c1 = 'c';
mySwap<int>(a, b);
mySwap2<int>();
cout << a << endl;
cout << b << endl;
double c = 3.14;
double d = 1.1;
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
using namespace std;
template <class T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
template< class T >
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;
}
}
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);
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
using namespace std;
//1、函数模板和普通函数的区别
template
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
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
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
using namespace std;
#include
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()
{
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
using namespace std;
#include
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()
{
Person<string > p1("孙悟空", 100);
p1.showPerson();
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
(6)类模板中成员函数创建时机
类模板中的成员函数 并不是一开始创建的,而是在运行阶段确定出T的数据类型才去创建
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
class Person1
{
public:
void showPerson1()
{
cout << "Person1 show 调用" << endl;
}
};
class Person2
{
public:
void showPerson2()
{
cout << "Person2 show 调用" << endl;
}
};
template<class T>
class MyClass
{
public:
void func1()
{
obj.showPerson1();
}
void func2()
{
obj.showPerson2();
}
T obj;
};
void test01()
{
MyClass <Person2> p1;
p1.func2();
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
(7)类模板做函数参数
1、指定传入类型
void doWork(Person &p)
2、参数模板化
2.1、template
2.2、void doWork2(Person &p)
3、整个类 模板化
3.1、template
3.2、void doWork3( T &p)
4、查看T数据类型
typeid(T).name()
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
template //类模板中 可以有默认参数
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 &p)
{
p.showPerson();
}
void test01()
{
Person p("孙悟空", 999);
doWork(p);
}
//2、参数模板化
template
void doWork2(Person &p)
{
cout << "T1数据类型: " << typeid(T1).name() << endl;
cout << "T2数据类型: " << typeid(T2).name() << endl;
p.showPerson();
}
void test02()
{
Personp("猪八戒", 998);
doWork2(p);
}
//3、整个类 模板化
template
void doWork3( T &p)
{
cout << "T的数据类型: " << typeid(T).name() << endl;
p.showPerson();
}
void test03()
{
Personp("唐僧", 10000);
doWork3(p);
}
int main(){
//test01();
//test02();
test03();
system("pause");
return EXIT_SUCCESS;
}
(8)类模板碰到继承的问题以及解决
1 必须要指定出父类中的T数据类型,才能给子类分配内存
2 template
3 class Son2 :public Base2
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
template<class T>
class Base
{
public:
T m_A;
};
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::showPerson()
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
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;
}
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
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
#include
#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 &p)
2友元类外实现
2.1 声明:
friend void printPerson2<>(Person &p);
2.2 实现:
2.2.1 template
2.2.2 void printPerson2(Person &p){ 。。。}
template
class Person;
template
void printPerson2(Person &p);
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
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
{
friend void printPerson(Person<T1, T2>& p)
{
cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}
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
using namespace std;
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;
Son* son2 = static_cast<Son*> (base);
Base* base2 = static_cast<Base*>(son);
}
void test03()
{
}
void test04()
{
Base* base = new Son;
Son* son = NULL;
Son* son2 = dynamic_cast<Son*>(base);
Base* base2 = dynamic_cast<Base*>(son);
}
void test05()
{
const int* p = NULL;
int* pp = const_cast<int*>(p);
const int* ppp = const_cast<const int*>(pp);
int num = 10;
int& numRef = num;
const int& num2 = const_cast<const int&>(numRef);
}
void test06()
{
int a = 10;
int* p = reinterpret_cast<int*>(a);
Base* base = NULL;
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
using namespace std;
#include
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)
{
Person p1;
Person p2;
throw MyException();
}
return a / b;
}
void test01()
{
int a = 10;
int b = 0;
try
{
myDivision(a, b);
}
catch (int)
{
cout << "int类型异常捕获" << endl;
}
catch (char)
{
cout << "char类型异常捕获" << endl;
}
catch (double)
{
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
#include
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
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
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 OutOfRangeException();
}
void test01()
{
try
{
doWork();
}
catch (BaseException & e)
{
e.printError();
}
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
(6)系统标准异常
1 引入头文件 #include
2 抛出越界异常 throw out_of_range(“…”)
3 获取错误信息 catch( exception & e ) e.what();
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include // 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
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
using namespace std;
void test01()
{
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();
if (c == '\n')
{
cout << "换行符遗留在缓冲区" << endl;
}
else
{
cout << "换行符不在缓冲区" << endl;
}
cout << buf << endl;
}
void test03()
{
char buf[1024] = { 0 };
cin.getline(buf, 1024);
char c = cin.get();
if (c == '\n')
{
cout << "换行符遗留在缓冲区" << endl;
}
else
{
cout << "换行符不在缓冲区" << endl;
}
cout << buf << endl;
}
void test04()
{
cin.ignore(2);
char c = cin.get();
cout << "c = " << c << endl;
}
void test05()
{
char c = cin.peek();
cout << "c = " << c << endl;
c = cin.get();
cout << "c = " << c << endl;
c = cin.get();
cout << "c = " << c << endl;
}
void test06()
{
char c = cin.get();
cin.putback(c);
char buf[1024] = { 0 };
cin.getline(buf, 1024);
cout << buf << endl;
}
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;
}
}
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();
cout << "输入有误,请重新输入: " << endl;
}
}
int main() {
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
using namespace std;
#include
void test01()
{
char buf[] = "hello world";
cout.write(buf, strlen(buf));
cout << "hello world" << endl;
}
void test02()
{
int number = 99;
cout.width(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;
}
void test03()
{
int number = 99;
cout << setw(20)
<< setfill('~')
<< setiosflags(ios::showbase)
<< setiosflags(ios::left)
<< hex
<< number
<< endl;
}
int main(){
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
using namespace std;
#include
#include
void test01()
{
ofstream ofs("./test.txt", ios::out | ios::trunc);
if (!ofs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
ofs << "姓名:孙悟空" << endl;
ofs << "年龄:999" << endl;
ofs << "性别:男" << endl;
ofs.close();
}
void test02()
{
ifstream ifs;
ifs.open("./test.txt", ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
char c;
while ( (c = ifs.get()) != EOF )
{
cout << c;
}
ifs.close();
}
int main(){
test02();
system("pause");
return EXIT_SUCCESS;
}