04C++核心编程
Day01
笔记
1 C++概述
1.1 C++两大编程思想
1.1.1 面向对象
1.1.2 泛型编程
1.2 移植性和标准
1.2.1 ANSI 在1998制定出C++第一套标准
2 c++初识
2.1 引入头文件 #include <iostream> 标准输入输出流
2.2 使用标准命名空间 using namespace std;
2.3 标准输出流对象 cout << “..” << 1234 << 3.14 << endl;
2.4 面向对象三大特性
2.4.1 封装、继承、多态
3 双冒号作用域运算符
3.1 ::代表作用域 如果前面什么都不添加 代表全局作用域
4 namespace命名空间
4.1 命名空间用途:解决名称冲突
4.2 命名空间下可以存放 : 变量、函数、结构体、类…
4.3 命名空间必须要声明在全局作用域
4.4 命名空间可以嵌套命名空
4.5 命名空间是开放的,可以随时将新成员添加到命名空间下
4.6 命名空间可以匿名的
4.7 命名空间可以起别名
5 using声明以及using编译指令
5.1 using声明
5.1.1 using KingGlory::sunwukongId
5.1.2 当using声明与 就近原则同时出现,出错,尽量避免
5.2 using编译指令
5.2.1 using namespace KingGlory;
5.2.2 当using编译指令 与 就近原则同时出现,优先使用就近
5.2.3 当using编译指令有多个,需要加作用域 区分
6 C++对C语言增强以及扩展
6.1 全局变量检测增强
6.1.1 int a ;
6.1.2 int a = 10; C下可以,C++重定义
6.2 函数检测增强
6.2.1 函数的返回值
6.2.2 形参类型
6.2.3 函数调用参数个数
6.3 类型转换检测增强
6.3.1 char * p = (char *)malloc(64) C++下必须等号左右一致类型
6.4 struct 增强
6.4.1 C++可以在结构体中放函数
6.4.2 创建结构体变量 可以简化关键字struct
6.5 bool数据类型扩展
6.5.1 C++才有bool类型
6.5.2 代表真 --- 1 true 假 ---- 0 false
6.5.3 sizeof = 1
6.6 三目运算符增强
6.6.1 C语言下返回的是值
6.6.2 C++语言下返回的是变量
6.7 const增强
6.7.1 C语言下
6.7.1.1 全局const 直接修改 失败 间接修改 语法通过,运行失败
6.7.1.2 局部 const 直接修改 失败 间接修改 成功
6.7.2 C++语言下
6.7.2.1 全局 const 和C结论一样
6.7.2.2 局部 const 直接修改失败 间接修改 失败
6.7.2.3 C++const可以称为常量
7 const 链接属性
7.1 C语言下const修饰的全局变量默认是外部链接属性
7.2 C++下const修饰的全局变量默认是内部链接属性,可以加extern 提高作用域
8 const分配内存情况
8.1 对const变量 取地址 ,会分配临时内存
8.2 使用普通变量 初始化 const变量
8.3 对于自定义数据类型
9 尽量用const代替define
9.1 define出的宏常量,没有数据类型、不重视作用域
10 引用
10.1 目的:起别名
10.2 语法: 类型(与原名类型必须一致) &别名 = 原名
10.3 引用必须要初始化
10.4 引用一旦初始化后,就不可以引向其他变量
10.5 建立对数组引用
10.5.1 直接建立引用
10.5.2 int arr[10];
10.5.3 int(&pArr)[10] = arr;
10.6 先定义出数组类型,再通过类型 定义引用
10.6.1 typedef int(ARRAY_TYPE)[10];
10.6.2 ARRAY_TYPE & pArr2 = arr;
11 参数的传递方式
11.1 值传递
11.2 地址传递
11.3 引用传递
12 注意事项
12.1 引用必须引一块合法内存空间
12.2 不要返回局部变量的引用
12.3 当函数返回值是引用时候,那么函数的调用可以作为左值进行运算
13 指针的引用
13.1 利用引用可以简化指针
13.2 可以直接用同级指针的 引用 给同级指针分配空间
14 常量的引用
14.1 const int &ref = 10;
14.2 // 加了const之后, 相当于写成 int temp = 10; const int &ref = temp;
14.3 常量引用的使用场景 修饰函数中的形参,防止误操作
15
Code
01.c++初识.cpp
#include <iostream> //标准输入输出流 i - input 输入 o - output 输出 stream 流 相当于 stdio.h
using namespace std; //使用 标准 命名空间
//#include <time.h>
//#include <ctime>
//
//#include <math.h>
//#include <cmath>
//程序入口函数
int main()
{
// cout 标准输出流对象
// << 左移 在C++下有了新的寓意 用于在cout后拼接输出的内容
// endl --- end line 刷新缓冲区 并且换行
cout << "hello world" << endl;
system("pause"); //阻塞
return EXIT_SUCCESS; //返回正常退出
}
02 双冒号作用域运算符.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
//using namespace std;
int atk = 1000;
void test01()
{
int atk = 2000;
std::cout << "atk = " << atk << std::endl;
// ::代表作用域 如果前面什么都不添加 代表全局作用域
std::cout << "全局 atk = " << ::atk << std::endl;
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
03 namespace命名空间
头文件
- game1.h
#include <iostream>
using namespace std;
namespace KingGlory
{
void goAtk();
}
- game2.h
#include <iostream>
using namespace std;
namespace LOL
{
void goAtk();
}
源文件
- game1.cpp
#include "game1.h"
void KingGlory::goAtk()
{
cout << "王者荣耀攻击实现" << endl;
}
- game2.cpp
#include "game2.h"
void LOL::goAtk()
{
cout << "LOL攻击实现" << endl;
}
- 03 namespace命名空间.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include "game1.h"
#include "game2.h"
//1、命名空间用途: 解决名称冲突
void test01()
{
KingGlory::goAtk();
LOL::goAtk();
}
//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;
cout << "B空间下的m_B = " << B::m_B << endl;
}
//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;
}
04 using声明以及using编译指令.c
#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();
test02();
system("pause");
return EXIT_SUCCESS;
}
05 C++对C语言增强和扩展_C语言.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//1、全局变量检测增强
int a;
int a = 10;
//2、函数检测增强 返回值没有检测 形参类型没有检测 函数调用参数个数没有检测
getRectS( w , 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(){
//test01();
//test04();
test05();
system("pause");
return EXIT_SUCCESS;
}
06 C++对C语言增强和扩展_C++语言.cpp
#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();
//test03();
//test04();
//test05();
test06();
system("pause");
return EXIT_SUCCESS;
}
07 C语言下const修饰全局变量默认是外部链接属性.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(){
extern const int g_a;
printf("g_a = %d\n", g_a);
system("pause");
return EXIT_SUCCESS;
}
08 C++下const修饰全局变量默认是内部链接属性
- 08 C++下const修饰全局变量默认是内部链接属性.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
int main(){
extern const int g_b;
cout << "g_b = " << g_b << endl;;
system("pause");
return EXIT_SUCCESS;
}
- test.cpp
extern const int g_b = 1000;//默认是内部链接属性 可以加关键字 extern 提高作用域
09 const分配内存情况.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>
//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;
}
//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;
}
10 引用基本语法.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
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; //引用必须要初始化
int &b = a;
//引用一旦初始化后,就不可以引向其他变量
int c = 100;
b = c; // 赋值
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
//对数组建立引用
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;
}
//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;
}
11 参数的传递方式.cpp
#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;
//2、不要返回局部变量的引用
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;
}
12 指针引用.cpp
#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();
test02();
system("pause");
return EXIT_SUCCESS;
}
13 常量的引用.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
void test01()
{
//int &ref = 10;
const int &ref = 10; // 加了const之后, 相当于写成 int temp = 10; const int &ref = temp;
int *p = (int *)&ref;
*p = 10000;
cout << ref << endl;
}
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;
}
Day02
笔记
1 设计一个类,求圆的周长
1.1 class + 类名 { 成员变量 成员函数 }
1.2 公共权限 public
1.3 设计成员属性
1.3.1 半径 int m_R
1.4 设计成员函数
1.4.1 获取圆周长 int calculateZC(){}
1.4.2 获取圆半径 int getR()
1.4.3 设置圆半径 void setR()
1.5 通过类创建对象过程 称为 实例化对象
2 内联函数
2.1 内联函数引出---宏缺陷
2.1.1 宏缺陷:
2.1.1.1 必须要加括号保证运算完整
2.1.1.2 即使加括号,有些运算依然与预期结果不符
2.1.2 普通函数不会出现缺陷
2.2 C++提供 内联函数代替宏函数
2.3 关键字 inline
2.4 在函数声明和实现中同时加入关键字 inline 才称为内联
2.5 在成员函数前 都隐式加了关键字inline
2.6 有些特殊情况下 ,写了关键字inline也不会按照内联方式处理
2.6.1 出现循环
2.6.2 出现判断
2.6.3 函数体过于庞大
2.6.4 对函数进行取地址
2.7 总结: 内联函数只是给编译器一个建议,但是编译器不一定接受这个建议,好的编译器会自己给短小的函数前加上关键字inline
3 函数的默认参数和占位参数
3.1 默认参数
3.1.1 可以给函数的形参添加默认值
3.1.2 语法 形参 类型 变量 = 默认值
3.1.3 int func(int a, int b = 10 , int c = 10)
3.1.4 注意事项 ,如果有一个位置有了默认参数,那么从这个位置起,从左到右都必须有默认值
3.1.5 函数的声明和实现 只能有一个 提供默认参数,不可以同时加默认参数
3.2 占位参数
3.2.1 只写一个类型进行占位,调用时候必须要传入占位值
3.2.2 void func2(int a , int = 1)
3.2.3 占位参数也可以有默认值
4 函数重载
4.1 满足条件
4.1.1 同一个作用域下
4.1.2 函数名称相同
4.1.3 函数参数个数、类型、顺序不同
4.2 函数的返回值 不可以作为重载条件
4.3 注意事项
4.3.1 加const和不加const的引用可以作为重载条件
4.3.2 函数重载碰到默认参数 注意避免二义性出现
5 extern C 浅析
5.1 用途:在C++中调用C语言文件
5.2 C++中有函数重载,会对函数名称做修饰,导致调用C语言的函数链接失败
5.3 利用extern C可以解决问题
5.3.1 方法1:
5.3.1.1 在C++代码中加入
5.3.1.2 告诉编译器 show函数用C语言方式 做链接
5.3.1.3 //extern "C" void show();
5.3.2 方法2:
5.3.2.1 在C语言的头文件中加入6行代码
5.3.2.2 #ifdef __cplusplus // 两个下划线 __ c plus plus
5.3.2.3 extern "C" {
5.3.2.4 #endif
5.3.2.5
5.3.2.6 #ifdef __cplusplus // 两个下划线 __ c plus plus
5.3.2.7 }
5.3.2.8 #endif
6 封装
6.1 C语言的封装
6.1.1 缺陷 将属性和行为分离
6.2 C++语言的封装
6.2.1 将属性和行为作为一个整体,来表现生活中的事物
6.2.2 将属性和行为 加以权限控制
6.3 访问权限
6.3.1 公共权限 public 类内 类外 都可以访问
6.3.2 私有权限 private 类内可以访问 类外不可以访问
6.3.3 保护权限 protected类内可以访问 类外不可以访问
6.4 class 默认权限 私有权限 而 struct默认权限是 公共权限
7 尽量将成员属性设置为私有
7.1 自己可以控制读写权限
7.2 可以对设置内容 加有效性验证
8
Code
01.设计一个类,求圆的周长.c
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>
//设计一个类,求圆的周长
const double PI = 3.14;
//class + 类名
//周长公式 : 2 * pi * m_R
class Circle
{
public: //公共权限
//类中的函数 称为 成员函数 成员方法
//求圆周长
double calculateZC()
{
return 2 * PI * m_R;
}
//设置半径
void setR(int r)
{
m_R = r;
}
//获取半径
int getR()
{
return m_R;
}
//类中的变量 称为成员变量 成员属性
//半径
int m_R;
};
void test01()
{
Circle c1; //通过类 创建一个对象 实例化对象
//给c1 半径赋值
//c1.m_R = 10;
c1.setR(10);
//求c1圆周长
cout << "圆的半径为: " << c1.getR() << endl;
cout << "圆的周长为: " << c1.calculateZC() << endl;
}
//2 设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号
class Student
{
public:
//设置姓名
void setName(string name)
{
m_Name = name;
}
//设置学号
void setId(int id)
{
m_Id = id;
}
//显示学生信息
void showStudent()
{
cout << "姓名:" << m_Name << " 学号: " << m_Id << endl;
}
//属性:
//姓名
string m_Name;
//学号
int m_Id;
};
void test02()
{
Student s1;
s1.m_Name = "张三";
s1.m_Id = 1;
cout << "姓名:" << s1.m_Name << " 学号: " << s1.m_Id << endl;
Student s2;
s2.setName("李四");
s2.setId(2);
s2.showStudent();
Student s3;
s3.setName("王五");
s3.setId(3);
s3.showStudent();
}
int main(){
//test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
02.内联函数引出-宏缺陷.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//宏缺陷1 : 必须要加括号保证运算完整
#define MYADD(x,y) ((x) +(y))
void test01()
{
int a = 10;
int b = 20;
int ret = MYADD(a, b) * 20;
cout << ret << endl;
}
//宏缺陷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;
}
03.函数的默认参数和占位参数.cpp
#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 , 10) << 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;
}
04.函数重载.cpp
#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;
}
05.externC浅析
头文件
- test.h
#ifdef __cplusplus // 两个下划线 __ c plus plus
extern "C" {
#endif
#include <stdio.h>
void show();
#ifdef __cplusplus
}
#endif
源文件
- test.cpp
#include "test.h"
void show()
{
printf("hello world\n");
}
- 05.externC浅析.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include "test.h"
//告诉编译器 show函数用C语言方式 做链接
//extern "C" void show();
void test01()
{
show();//_Z4showv;在C++中有函数重载会修饰函数名,但是show是c语言文件,因此链接失败
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
06.C语言下的封装.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct Person
{
char name[64];
int age;
};
void PersonEat(struct Person * p)
{
printf("%s在吃人饭\n",p->name);
}
void test01()
{
struct Person p;
strcpy(p.name, "张三");
p.age = 10;
PersonEat(&p);
}
struct Dog
{
char name[64];
int age;
};
void DogEat(struct Dog * dog)
{
printf("%s在吃狗粮\n", dog->name);
}
void test02()
{
struct Dog d;
strcpy(d.name, "旺财");
d.age = 100;
DogEat(&d);
struct Person p;
strcpy(p.name, "老王");
DogEat(&p);
}
int main(){
// test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
07.C++语言下的封装.cpp
#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;
}
08.尽量将成员属性设置为私有.cpp
#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;
}
Day03
笔记
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 分文件编写 点和圆类
3 构造函数和析构函数
3.1 构造函数
3.1.1 没有返回值 不用写void
3.1.2 函数名 与 类名相同
3.1.3 可以有参数 ,可以发生重载
3.1.4 构造函数 由编译器自动调用一次 无须手动调用
3.2 析构函数
3.2.1 没有返回值 不用写void
3.2.2 函数名 与类名相同 函数名前 加 ~
3.2.3 不可以有参数 ,不可以发生重载
3.2.4 析构函数 也是由编译器自动调用一次,无须手动调用
4 构造函数的分类和调用
4.1 分类
4.1.1 按照参数分类: 有参 无参(默认)
4.1.2 按照类型分类: 普通 拷贝构造 ( const Person & p )
4.2 调用
4.2.1 括号法
4.2.2 显示法
4.2.3 隐式法
4.3 注意事项
4.3.1 不要用括号法 调用无参构造函数 Person p3(); 编译器认为代码是函数的声明
4.3.2 不要用拷贝构造函数 初始化 匿名对象 Person(p3); 编译器认为 Person p3对象实例化 如果已经有p3 p3就重定义
4.4 匿名对象 特点: 当前行执行完后 立即释放
5 拷贝构造函数的调用时机
5.1 用已经创建好的对象来初始化新的对象
5.2 值传递的方式 给函数参数传值
5.3 以值方式 返回局部对象
6 构造函数的调用规则
6.1 1、编译器会给一个类 至少添加3个函数 默认构造(空实现) 析构函数(空实现) 拷贝构造(值拷贝)
6.2 2、如果我们自己提供了 有参构造函数,编译器就不会提供默认构造函数,但是依然会提供拷贝构造函数
6.3 3、如果我们自己提供了 拷贝构造函数,编译器就不会提供其他构造函数
7 深拷贝与浅拷贝的问题以及解决
7.1 如果有属性开辟到堆区,利用编译器提供拷贝构造函数会调用浅拷贝带来的析构重复释放堆区内存的问题
7.2 利用深拷贝解决浅拷贝问题
7.3 自己提供拷贝构造函数,实现深拷贝
8 初始化列表
8.1 可以利用初始化列表语法 对类中属性进行初始化
8.2 语法:构造函数名称后 : 属性(值), 属性(值)...
8.2.1 Person(int a, int b, int c) : m_A(a), m_B(b), m_C(c)
9 类对象作为类中成员
9.1 当其他类对象 作为本类成员,先构造其他类对象,再构造自身,析构的顺序和构造相反
10 explicit关键字
10.1 explicit用途: 防止利用隐式类型转换方式来构造对象
11 new和delete
11.1 malloc 和 new 区别
11.1.1 malloc 和 free 属于 库函数 new 和delete属于 运算符
11.1.2 malloc不会调用构造函数 new会调用构造函数
11.1.3 malloc返回void* C++下要强转 new 返回创建的对象的指针
11.2 注意事项 不要用void*去接受new出来的对象,利用void*无法调用析构函数
11.3 利用new创建数组
11.3.1 Person * pPerson = new Person[10];
11.3.2 释放数组时候 需要加[]
11.3.3 delete [] pPerson;
11.4 堆区开辟数组,一定会调用默认构造函数
11.5 栈上开辟数组,可不可以没有默认构造,可以没有默认构造
12
Code
01 立方体类设计案例.cpp
#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;
}
02 点和圆关系案例
头文件
- point.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//点类
class Point
{
public:
//设置 x 、y
void setX(int x);
void setY(int y);
//获取 x 、y
int getX();
int getY();
private:
int m_X;
int m_Y;
};
- circle.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include "point.h"
//圆类
class Circle
{
public:
//设置半径
void setR(int r);
//获取半径
int getR();
//设置圆心
void setCenter(Point p);
//获取圆心
Point getCenter();
//判断点和圆关系
void isInCircleByClass(Point p);
private:
int m_R; //半径
Point m_Center; // 圆心
};
源文件
- point.cpp
#include "point.h"
//设置 x 、y
void Point::setX(int x)
{
m_X = x;
}
void Point::setY(int y)
{
m_Y = y;
}
//获取 x 、y
int Point::getX()
{
return m_X;
}
int Point::getY()
{
return m_Y;
}
- circle.cpp
#include "circle.h"
//圆类
//设置半径
void Circle::setR(int r)
{
m_R = r;
}
//获取半径
int Circle::getR()
{
return m_R;
}
//设置圆心
void Circle::setCenter(Point p)
{
m_Center = p;
}
//获取圆心
Point Circle::getCenter()
{
return m_Center;
}
//判断点和圆关系
void Circle::isInCircleByClass(Point p)
{
int distance = (m_Center.getX() - p.getX())*(m_Center.getX() - p.getX()) + (m_Center.getY() - p.getY()) *(m_Center.getY() - p.getY());
int rDistance = m_R * m_R;
if (distance == rDistance)
{
cout << "成员函数判断:点在圆上" << endl;
}
else if (distance > rDistance)
{
cout << "成员函数判断:点在圆外" << endl;
}
else
{
cout << "成员函数判断:点在圆内" << endl;
}
}
- 02 点和圆关系案例.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include "circle.h"
#include "point.h"
/*
设计一个圆形类(AdvCircle),和一个点类(Point),计算点和圆的关系。
假如圆心坐标为x0, y0, 半径为r,点的坐标为x1, y1:
*/
////点类
//class Point
//{
//public:
//
// //设置 x 、y
// void setX(int x)
// {
// m_X = x;
// }
// void setY(int y)
// {
// m_Y = y;
// }
// //获取 x 、y
// int getX()
// {
// return m_X;
// }
// int getY()
// {
// return m_Y;
// }
//private:
// int m_X;
// int m_Y;
//};
//
////圆类
//class Circle
//{
//public:
// //设置半径
// void setR(int r)
// {
// m_R = r;
// }
// //获取半径
// int getR()
// {
// return m_R;
// }
//
// //设置圆心
// void setCenter(Point p)
// {
// m_Center = p;
// }
//
// //获取圆心
// Point getCenter()
// {
// return m_Center;
// }
//
// //判断点和圆关系
// void isInCircleByClass(Point p)
// {
//
// int distance = (m_Center.getX() - p.getX())*(m_Center.getX() - p.getX()) + (m_Center.getY() - p.getY()) *(m_Center.getY() - p.getY());
//
// int rDistance = m_R * m_R;
//
// if (distance == rDistance)
// {
// cout << "成员函数判断:点在圆上" << endl;
// }
// else if (distance > rDistance)
// {
// cout << "成员函数判断:点在圆外" << endl;
// }
// else
// {
// cout << "成员函数判断:点在圆内" << endl;
// }
// }
//
//private:
//
// int m_R; //半径
//
// Point m_Center; // 圆心
//};
//利用全局函数 判断点和圆关系
void isInCircle( Circle c , Point p )
{
int distance = (c.getCenter().getX() - p.getX())*(c.getCenter().getX() - p.getX()) + (c.getCenter().getY() - p.getY()) *(c.getCenter().getY() - p.getY());
int rDistance = c.getR() * c.getR();
if (distance == rDistance)
{
cout << "点在圆上" << endl;
}
else if (distance > rDistance)
{
cout << "点在圆外" << endl;
}
else
{
cout << "点在圆内" << endl;
}
}
void test01()
{
Point p;
p.setX(10);
p.setY(9);
Circle c;
Point pCenter;
pCenter.setX(10);
pCenter.setY(0);
c.setCenter(pCenter);
c.setR(10);
isInCircle(c, p);
c.isInCircleByClass(p);
}
class A
{
virtual void func() = 0;
};
class B :public A
{
};
class C :public B
{
};
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
03 构造函数和析构函数.cpp
#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;
}
04 构造函数的分类以及调用.cpp
#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;
}
05 拷贝构造函数的调用时机.cpp
#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;
}
06 构造函数的调用规则.cpp
#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;
}
07 深拷贝与浅拷贝问题以及解决.cpp
#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()
{
Person p("德玛西亚", 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;
}
08 初始化列表.cpp
#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;
}
09 类对象作为类成员.cpp
#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关键字使用.cpp
#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使用.cpp
#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;
}
Day04
笔记
1 静态成员
1.1 静态成员变量
1.1.1 所有对象都共享同一份数据
1.1.2 编译阶段就分配内存
1.1.3 类内声明、类外初始化
1.1.4 访问方式有两种:通过对象访问、通过类名访问
1.1.5 静态成员变量也是有访问权限
1.2 静态成员函数
1.2.1 所有对象都共享同一份函数
1.2.2 静态成员函数 只可以访问 静态成员变量,不可以访问非静态成员变量
1.2.3 静态成员函数 也是有访问权限的
1.2.4 静态成员函数 有两种访问方式:通过对象 、通过类名
2 单例模式 – 主席类案例
2.1 通过一个类 只能实例化唯一的一个对象
2.2 私有化
2.2.1 默认构造
2.2.2 拷贝构造
2.2.3 唯一实例指针
2.3 对外提供 getInstance 接口,将指针返回
3 单例模式 – 打印机案例
3.1 和主席类案例一样设计单例模式
3.2 提供打印功能并且统计打印次数
4 C++对象模型初探
4.1 类中的成员变量 和 成员函数 是分开存储的
4.2 只有非静态成员变量 属于类对象上
4.3 空类的sizeof结果 1
5 this指针
5.1 this指针 指向 被调用的成员函数 所属的对象
5.2 this指针可以解决名称冲突
5.3 this指针 隐式加在每个成员函数中
5.4 *this 就是本体
5.5 p1.personAddPerson(p2).personAddPerson(p2).personAddPerson(p2); //链式编程
6 空指针访问成员函数
6.1 如果成员函数中没有用到this指针,可以用空指针调用成员函数
6.2 如果成员函数中用到了this,那么这个this需要加判断,防止代码down掉
7 常对象和常函数
7.1 常函数
7.1.1 成员函数 声明后面加const
7.1.2 void showPerson() const
7.1.3 const目的是为了修饰成员函数中的this指针,让指针指向的值不可以修改
7.1.4 有些属性比较特殊,依然在常函数或者常对象中可以修改,需要加入关键字 mutable
7.2 常对象
7.2.1 const Person p
7.2.2 常对象也不许修改成员属性
7.2.3 常对象只能调用常函数
7.3 对于成员函数 ,可不可以 用static 和 const同时修饰 ,不可以
8 友元
8.1 全局函数作为友元函数
8.1.1 利用friend关键字让全局函数 goodGay作为本类好朋友,可以访问私有成员
8.1.2 friend void goodGay(Building * buliding);
8.2 类作为友元类
8.2.1 让goodGay类作为 Building的好朋友,可以访问私有成员
8.2.2 friend class GoodGay;
8.3 类中的成员函数作为友元函数
8.3.1 让GoodGay类中的 visit成员函数作为友元
8.3.2 friend void GoodGay::visit();
Code
01 静态成员.cpp
#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;
}
02 单例模式-主席类案例.cpp
#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;
}
03 单例模式 - 打印机案例.cpp
#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;
}
04 C++对象初探.cpp
#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;
}
05 this指针的使用.cpp
#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;
}
06 空指针访问成员函数.cpp
#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;
}
07 常函数和常对象.cpp
#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;
}
08 全局函数做友元函数.cpp
#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;
}
09 类作为友元类.cpp
#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;
}
10 成员函数作为友元函数.cpp
#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;
}
Day05
笔记
1 强化训练-数组类封装
1.1 设计类 myArray
1.2 属性
1.2.1 int m_Capacity数组容量
1.2.2 int m_Size 数组大小
1.2.3 int pAddress 维护真实在堆区创建的数组指针
1.3 行为
1.3.1 默认构造
1.3.2 有参构造
1.3.3 拷贝构造
1.3.4 析构
1.3.5 根据位置 设置数据
1.3.6 根据位置 获取数据
1.3.7 尾插
1.3.8 获取数组容量
1.3.9 获取数组大小
2 加号运算符重载
2.1 对于内置的数据类型,编译器知道如何进行运算
2.2 但是对于自定义数据类型,编译器不知道如何运算
2.3 利用运算符重载 可以让符号有新的含义
2.4 利用加号重载 实现p1 + p2 Person数据类型相加操作
2.5 利用成员函数 和 全局函数 都可以实现重载
2.6 关键字 operator +
2.7 成员本质 p1.operator+(p2)
2.8 全局本质 operator+(p1,p2)
2.9 简化 p1 + p2
2.10 运算符重载 也可以发生函数重载
3 左移运算符重载
3.1 不要滥用运算符重载,除非有需求
3.2 不能对内置数据类型进行重载
3.3 对于自定义数据类型,不可以直接用 cout << 输出
3.4 需要重载 左移运算符
3.5 如果利用成员 函数重载 ,无法实现让cout 在左侧,因此不用成员重载
3.6 利用全局函数 实现左移运算符重载
3.6.1 ostream& operator<<(ostream &cout, Person & p1)
3.7 如果想访问类中私有内存,可以配置友元实现
4 递增运算符重载
4.1 前置递增
4.1.1 MyInter& operator++()
4.2 后置递增
4.2.1 MyInter operator++(int)
4.3 前置++ 效率高于 后置++ 效率 ,因为后置++会调用拷贝构造,创建新的数据
5 指针运算符重载
5.1 智能指针
5.2 用途: 托管new出来的对象的释放
5.3 设计smartPoint智能指针类,内部维护 Person * ,在析构时候释放堆区new出来的person对象
5.4 重载 -> * 让 sp智能指针用起来向真正的指针
6 赋值运算符重载
6.1 编译器会默认个一个类添加4个函数
6.1.1 默认构造、析构 、 拷贝构造(值拷贝) 、 operator=(值拷贝)
6.2 如果类中有属性创建在堆区,利用编译器提供的 = 赋值运算就会出现 堆区内存重复释放的问题
6.3 解决方案:利用深拷贝 重载 =运算符
6.4 Person& operator=( const Person &p)
7 []运算符重载
7.1 int& operator[](int index);
7.2 实现访问数组时候利用[] 访问元素
8
Code
01 强化训练-数组类封装
头文件
- myArray.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class MyArray
{
public:
MyArray(); //默认构造 可以给100容量
MyArray(int capacity); //有参构造
MyArray(const MyArray & arr); //拷贝构造
//尾插法
void pushBack(int val);
//根据位置设置数据
void setData(int pos, int val);
//根据位置获取数据
int getData(int pos);
//获取数组容量
int getCapcity();
//获取数组大小
int getSize();
//析构
~MyArray();
//重载[]运算符
int& operator[](int index);
private:
int m_Capacity; //数组容量
int m_Size; //数组大小
int *pAddress; //真实在堆区开辟的数组的指针
};
源文件
- myArray.cpp
#include "myArray.h"
MyArray::MyArray()
{
cout << "默认构造函数调用" << endl;
this->m_Capacity = 100;
this->m_Size = 0;
this->pAddress = new int[this->m_Capacity];
}
MyArray::MyArray(int capacity)
{
cout << "有参构造函数调用" << endl;
this->m_Capacity = capacity;
this->m_Size = 0;
this->pAddress = new int[this->m_Capacity];
}
MyArray::MyArray(const MyArray & arr)
{
cout << "拷贝构造函数调用" << endl;
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
//this->pAddress = arr.pAddress;
this->pAddress = new int[arr.m_Capacity];
for (int i = 0; i < m_Size;i++)
{
this->pAddress[i] = arr.pAddress[i];
}
}
//尾插法
void MyArray::pushBack(int val)
{
this->pAddress[this->m_Size] = val;
this->m_Size++;
}
//根据位置设置数据
void MyArray::setData(int pos, int val)
{
this->pAddress[pos] = val;
}
//根据位置获取数据
int MyArray::getData(int pos)
{
return this->pAddress[pos];
}
//获取数组容量
int MyArray::getCapcity()
{
return this->m_Capacity;
}
//获取数组大小
int MyArray::getSize()
{
return this->m_Size;
}
//析构
MyArray::~MyArray()
{
if (this->pAddress != NULL)
{
cout << "析构函数调用" << endl;
delete [] this->pAddress;
this->pAddress = NULL;
}
}
int& MyArray::operator[](int index)
{
return this->pAddress[index];
}
- 01 强化训练-数组类封装.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include "myArray.h"
void test01()
{
MyArray arr;
for (int i = 0; i < 10;i++)
{
arr.pushBack(i);
}
for (int i = 0; i < arr.getSize(); i ++)
{
cout << arr.getData(i) << endl;
}
MyArray arr2(arr);
for (int i = 0; i < arr2.getSize(); i++)
{
cout << arr2.getData(i) << endl;
}
arr.setData(0, 1000);
cout << "arr 0号位置数据为: " << arr.getData(0) << endl;
for (int i = 0; i < 10; i++)
{
cout << arr.getData(i) << endl;
}
cout << "数组容量为: " << arr.getCapcity() << endl;
cout << "数组大小为: " << arr.getSize() << endl;
//小任务: 利用[]方式去索引数组中的元素,可读可写
cout << "---------------------" << endl;
arr[0] = 10000;
cout << arr[0] << endl;
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
02 加号运算符重载.cpp
#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;
}
03 左移运算符重载.cpp
#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;
}
04 递增运算符重载.cpp
#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;
}
05 指针运算符重载.cpp
#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;
}
06 赋值运算符重载.cpp
#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;
}
Day06
笔记
1 关系运算符重载
1.1 对于自定义数据类型,编译器不知道如果进行比较
1.2 重载 == !=号
1.3 bool operator==( Person & p)
1.4 bool operator!=(Person & p)
2 函数调用运算符重载
2.1 重载 ()
2.2 使用时候很像函数调用,因此称为仿函数
2.3 void operator()(string text)
2.4 int operator()(int a,int b)
2.5 仿函数写法不固定,比较灵活
2.6 cout << MyAdd()(1, 1) << endl; // 匿名函数对象 特点:当前行执行完立即释放
3 不要重载 && 和 ||
3.1 原因是无法实现短路特性
3.2 建议:将<< 和 >>写成全局函数,其他可重载的符号写到成员即可
4 强化训练-字符串类封装
4.1 myString类 实现自定义的字符串类
4.2 属性
4.2.1 char * pString; 维护 在堆区真实开辟的字符数组
4.2.2 int m_Size; 字符串长度
4.3 行为
4.3.1 有参构造 MyString(char * str)
4.3.2 拷贝构造 MyString(const MyString & str);
4.3.3 析构 ~MyString();
4.4 重载<< 运算符
4.5 重载 >> 运算符
4.6 重载 = 赋值运算
4.7 重载 [] str[0] 按照索引位置设置获取字符
4.8 重载 + 字符串拼接
4.9 重载 == 对比字符串
5 继承基本语法
5.1 继承优点:减少重复的代码,提高代码复用性
5.2 // 语法: class 子类 : 继承方式 父类
5.3 // News 子类 派生类
5.4 // BasePage 父类 基类
6 继承方式
6.1 公共继承
6.1.1 父类中公共权限,子类中变为公共权限
6.1.2 父类中保护权限,子类中变为保护权限
6.1.3 父类中私有权限,子类访问不到
6.2 保护继承
6.2.1 父类中公共权限,子类中变为保护权限
6.2.2 父类中保护权限,子类中变为保护权限
6.2.3 父类中私有权限,子类访问不到
6.3 私有继承
6.3.1 父类中公共权限,子类中变为私有权限
6.3.2 父类中保护权限,子类中变为私有权限
6.3.3 父类中私有权限,子类访问不到
7 继承中的对象模型
7.1 父类中的私有属性,子类是继承下去了,只不过由编译器给隐藏了,访问不到
7.2 可以利用开发人员工具查看对象模型
7.3 C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts
7.4 打开开发人员命令工具
7.5 跳转盘符 E:
7.6 跳转文件路径 cd到文件路径下
7.7 cl /d1 reportSingleClassLayout类名 文件名
8 继承中的构造和析构
8.1 先调用父类构造,再调用其他成员构造, 再调用自身构造 ,析构的顺序与构造相反
8.2 利用初始化列表语法 显示调用父类中的其他构造函数
8.3 父类中 构造、析构、拷贝构造 、operator= 是不会被子类继承下去的
9 继承中的同名成员处理
9.1 我们可以利用作用域 访问父类中的同名成员
9.2 当子类重新定义了父类中的同名成员函数,子类的成员函数会 隐藏掉父类中所有重载版本的同名成员,可以利用作用域显示指定调用
10 继承中的同名 静态成员处理
10.1 结论和 非静态成员 一致
10.2 只不过调用方式有两种
10.2.1 通过对象
10.2.2 通过类名
10.2.2.1 通过类名的方式 访问 父类作用域下的m_A静态成员变量
10.2.2.2 Son::Base::m_A
11 多继承基本语法
11.1 class 子类 : 继承方式 父类1 , 继承方式 父类2
11.2 当多继承的两个父类中有同名成员,需要加作用域区分
12 菱形继承
12.1 两个类有公共的父类 和共同的子类 ,发生菱形继承
12.2 菱形继承导致数据有两份,浪费资源
12.3 解决方案:利用虚继承可以解决菱形继承问题
12.3.1 class Sheep : virtual public Animal{};
12.4 //当发生虚继承后,sheep和tuo类中 继承了一个 vbptr指针 虚基类指针 指向的是一个 虚基类表 vbtable
12.5 //虚基类表中记录了 偏移量 ,通过偏移量 可以找到唯一的一个m_Age
12.6 利用地址偏移找到 vbtable中的偏移量 并且访问数据
13
Code
01 关系运算符重载.cpp
#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;
}
02 函数调用运算符重载.cpp
#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;
}
03 强化训练-字符串类封装
头文件
- myString.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class MyString
{
//左移运算符友元
friend ostream& operator<<(ostream & cout, MyString & str);
//右移运算符 友元
friend istream& operator>>(istream & cin, MyString & str);
public:
//有参构造
MyString(char * str);
//拷贝构造
MyString(const MyString & str);
//重载=运算符
MyString& operator=(const char * str);
MyString& operator=(const MyString & str);
//重载[]运算符
char& operator[](int index);
//重载+运算符
MyString operator+(const char * str);
MyString operator+(const MyString&str);
//重载==运算符
bool operator==(const char *str);
bool operator==(const MyString &str);
//析构
~MyString();
private:
char * pString; //维护在堆区开辟的字符数组
int m_Size; //字符串长度 不统计\0
};
源文件
- myString.cpp
#include "myString.h"
//重载左移运算符
ostream& operator<<(ostream & cout , MyString & str)
{
cout << str.pString;
return cout;
}
//重载右移运算符
istream& operator>>(istream & cin, MyString & str)
{
//先清空原来堆区数据
if (str.pString)
{
delete[] str.pString;
str.pString = NULL;
}
char buf[1024];//开辟临时数组 记录用户输入内容
cin >> buf;
str.pString = new char[strlen(buf) + 1];
strcpy(str.pString, buf);
str.m_Size = strlen(buf);
return cin;
}
MyString::MyString(char * str)
{
//cout << "MyString有参构造函数调用" << endl;
this->pString = new char[strlen(str) + 1];
strcpy(this->pString, str);
this->m_Size = strlen(str);
}
MyString::MyString(const MyString & str)
{
//cout << "拷贝构造函数调用" << endl;
this->pString = new char[strlen(str.pString)+1];
strcpy(this->pString, str.pString);
this->m_Size = str.m_Size;
}
MyString& MyString::operator=(const char * str)
{
//先判断原来堆区释放有内容,如果有先释放
if (this->pString != NULL)
{
delete[]this->pString;
this->pString = NULL;
}
this->pString = new char[strlen(str) + 1];
strcpy(this->pString, str);
this->m_Size = strlen(str);
return *this;
}
MyString& MyString::operator=(const MyString & str)
{
if (this->pString != NULL)
{
delete[]this->pString;
this->pString = NULL;
}
this->pString = new char[strlen(str.pString) + 1];
strcpy(this->pString, str.pString);
this->m_Size = strlen(str.pString);
return *this;
}
char& MyString::operator[](int index)
{
return this->pString[index];
}
MyString MyString::operator+(const char * str)
{
//本身 abc 传入 def
//计算开辟内存大小
int newSize = this->m_Size + strlen(str) + 1;
char * temp = new char[newSize];
memset(temp, 0, newSize);
strcat(temp, this->pString);
strcat(temp, str);
MyString newString = temp;
delete[] temp;
return newString;
}
MyString MyString::operator+(const MyString&str)
{
int newSize = this->m_Size + strlen(str.pString) + 1;
char * temp = new char[newSize];
memset(temp, 0, newSize);
strcat(temp, this->pString);
strcat(temp, str.pString);
MyString newString = temp;
delete[] temp;
return newString;
}
bool MyString::operator==(const char *str)
{
if ( strcmp( this->pString , str) == 0 )
{
return true;
}
return false;
}
bool MyString::operator==(const MyString &str)
{
if (strcmp(this->pString, str.pString) == 0)
{
return true;
}
return false;
}
MyString::~MyString()
{
if (this->pString != NULL)
{
//cout << "析构调用" << endl;
delete[] this->pString;
this->pString = NULL;
}
}
- 03 强化训练-字符串类封装.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//#include <string>
#include "myString.h"
void test01()
{
MyString str = "abc";
cout << str << endl;
cout << "请重新给str赋值:" << endl;
cin >> str;
cout << "str 新的值为: " << str << endl;
MyString str2 = str;
cout << "str2 = " << str2 << endl;
}
void test02()
{
MyString str = "abcd";
MyString str2 = "aaa";
str2 = str;
cout << "str2 = " << str2 << endl;
cout << "str2[0] = " << str2[0] << endl;
str2[0] = 'z';
cout << "str2[0]改为z后输出: " << str2 << endl;
MyString str3 = "abc";
MyString str4 = "def";
MyString str5 = str3 + str4;
MyString str6 = str5 + "ghe";
cout << "str5 = " << str5 << endl;
cout << "str6 = " << str6 << endl;
if (str5 == str6)
{
cout << "str5 == str6" << endl;
}
else
{
cout << "str5 != str6" << endl;
}
if ( str6 == "abcdefghe")
{
cout << "str6 = abcdefghe" << endl;
}
else
{
cout << "str6 != abcdefghe" << endl;
}
}
int main(){
//test01();
test02();
//int a = 10;
//cin >> a;
//cout << "a = " << a << endl;
system("pause");
return EXIT_SUCCESS;
}
04 继承基本语法.cpp
#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;
}
05 继承方式.cpp
#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;
}
06 继承中的对象模型.cpp
#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;
}
07 继承中的构造和析构.cpp
#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;
}
08 继承中的同名成员处理.cpp
#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;
}
09 继承中的同名静态成员处理.cpp
#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;
}
10 多继承基本语法.cpp
#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;
}
11 菱形继承问题以及解决.cpp
#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;
}
Day07
笔记
1 静态联编动态联编
1.1 静态多态和动态多态
1.2 静态多态:函数重载,运算符重载
1.3 动态多态:
1.3.1 先有继承关系
1.3.2 父类中有虚函数,子类重写父类中的虚函数
1.3.3 父类的指针或引用 指向子类的对象
1.4 静态多态在编译阶段绑定地址,地址早绑定,静态联编
1.5 动态多次在运行阶段绑定地址,地址晚绑定,动态联编
2 多态原理
2.1 当父类写了虚函数后,类内部结构发生改变,多了一个vfptr
2.2 vfptr 虚函数表指针 ---- > vftable 虚函数表
2.3 虚函数表内部记录着 虚函数的入口地址
2.4 当父类指针或引用指向子类对象,发生多态,调用是时候从虚函数中找函数入口地址
2.5 虚函数 关键字 virtual
2.6 利用指针的偏移调用 函数
2.6.1 ((void(*)()) (*(int *)*(int *)animal)) ();
2.6.2 typedef void( __stdcall *FUNPOINT)(int);
2.6.3 (FUNPOINT (*((int*)*(int*)animal + 1)))(10);
3 多态案例 - 计算器案例
3.1 设计抽象计算器类,分别实现加减乘计算,继承于抽象计算器类,重写虚函数
3.2 利用多态可以调用不同计算器
3.3 多态的好处
3.3.1 代码可读性强
3.3.2 组织结构清晰
3.3.3 扩展性强
3.4 开闭原则: 对扩展进行开放 对修改进行关闭
4 纯虚函数和抽象类
4.1 语法: virtual int getResult() = 0;
4.2 如果一个类中包含了纯虚函数,那么这个类就无法实例化对象了,这个类通常我们称为 抽象类
4.3 抽象类的子类 必须要重写 父类中的纯虚函数,否则也属于抽象类
5 虚析构和纯虚析构
5.1 虚析构语法:
5.1.1 virtual ~Animal(){}
5.1.2 如果子类中有指向堆区的属性,那么要利用虚析构技术 在delete的时候 调用子类的析构函数
5.2 纯虚析构语法:
5.2.1 virtual ~Animal() = 0;
5.2.2 Animal::~Animal(){ .. }
5.2.3 纯虚析构 需要有声明 也需要有实现
5.2.4 如果一个类中 有了 纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了
6 向上类型转换和向下类型转换
6.1 父转子 向下类型转换 不安全
6.2 子转父 向上类型转换 安全
6.3 如果发生多态,那么转换永远都是安全的
7 重载、重写、重定义
7.1 重载
7.1.1 函数重载
7.1.2 同一个作用域下,函数名称相同,参数个数、顺序、类型不同 (在同一作用域内)
7.2 重写
7.2.1 子类重写父类中的虚函数,函数返回值、函数名、形参列表完全一致称为重写 (多态中调用)
7.3 重定义
7.3.1 子类重新定义父类中的同名成员函数,隐藏掉父类中同名成员函数,如果想调用加作用域 (子类继承父类是有和父类同名的方法)
8 多态案例2 - 电脑组装案例
8.1 利用多态实现 电脑组装
9
Code
01 静态联编和动态联编.cpp
#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;
}
02 多态案例-计算器案例.cpp
#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;
}
03 虚析构和纯虚析构.cpp
#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()
{
Animal * animal = new Cat("Tom");
animal->speak();
delete animal;
}
int main(){
test01();
//Animal aaa; 在Animal中有了纯虚析构,也属于抽象类
system("pause");
return EXIT_SUCCESS;
}
04 多态案例-电脑组装案例.cpp
#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;
}
04C++核心编程的更多相关文章
- 04C++核心编程(二-泛型编程)
Day08 笔记 1 函数模板 1.1 泛型编程 – 模板技术 特点:类型参数化 1.2 template< typename T > 告诉编译器后面紧跟着的函数或者类中出现T,不要报错, ...
- Qt on Android 核心编程
Qt on Android 核心编程(最好看的Qt编程书!CSDN博主foruok倾力奉献!) 安晓辉 著 ISBN 978-7-121-24457-5 2015年1月出版 定价:65.00元 4 ...
- windows核心编程 - 线程同步机制
线程同步机制 常用的线程同步机制有很多种,主要分为用户模式和内核对象两类:其中 用户模式包括:原子操作.关键代码段 内核对象包括:时间内核对象(Event).等待定时器内核对象(WaitableTim ...
- windows核心编程---第九章 同步设备IO与异步设备IO之同步IO
同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备的通知.而IO完成端口就是实现这种通知的很 ...
- windows核心编程---第八章 使用内核对象进行线程同步
使用内核对象进行线程同步. 前面我们介绍了用户模式下线程同步的几种方式.在用户模式下进行线程同步的最大好处就是速度非常快.因此当需要使用线程同步时用户模式下的线程同步是首选. 但是用户模式下的线程同步 ...
- python核心编程(第二版)习题
重新再看一遍python核心编程,把后面的习题都做一下.
- Python核心编程这本书的一些错误
<Python核心编程第二版>这本书比<Python基础教程第二版修订版>详细很多,丰富了很多细节,虽然它是一本经典的入门书,但我发现还是存在一些明显的错误.在面向对象编程这一 ...
- windows核心编程---第二章 字符和字符串处理
使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是指UTF-16.也 ...
- 回忆读windows 核心编程
看<windows 核心编程> 第五版到纤程了,下一章节即将介绍内存体系编程.如果做window平台下的开发,我感觉此书一定要读.记得开始讲解了window的基础,然后讲解内核对象.内核对 ...
随机推荐
- Linux 0.11源码阅读笔记-内存管理
内存管理 Linux内核使用段页式内存管理方式. 内存池 物理页:物理空闲内存被划分为固定大小(4k)的页 内存池:所有空闲物理页组成内存池,以页为单位进行分配回收.并通过位图记录了每个物理页是否空闲 ...
- php运用validate+ajax检测用户名是否已存在
前提:如果还不知道什么是validate,请前往这里 一.remote rules: username:{ required:true, minlength:8, maxlength:8, remot ...
- Coursera 学习笔记|Machine Learning by Standford University - 吴恩达
/ 20220404 Week 1 - 2 / Chapter 1 - Introduction 1.1 Definition Arthur Samuel The field of study tha ...
- 实现WebMvcConfigurer接口扩展Spring MVC的功能
前言: 先查看WebMvcConfigurer接口中都定义了哪些内容 public interface WebMvcConfigurer { default void configurePathMat ...
- 微信小程序,制作属于自己的Icon图标
前言 最近在接手一个微信小程序,发现里面的图标都是使用的image组件,看起来非常别扭,加载也不太顺畅. 就想着看看微信有没有类似自带的图标库可以使用. 有是有,就是太少了,翻来翻去好像也就 8 种, ...
- MongoDB 集群-主从复制(一主二从)
MongoDB 集群-主从复制(一主二从) 官方文档 https://docs.mongodb.com/manual/tutorial/deploy-replica-set/ https://docs ...
- python3拉勾网爬虫之(您操作太频繁,请稍后访问)
你是否经历过这个:那就对了~因为需要post和相关的cookie来请求~所以,一个简单的代码爬拉钩~~~
- 3.Docker容器学习之新手基础使用
原文地址: http://blog.weiyigeek.top/2019/5/2-docker%E5%AD%A6%E4%B9%A0%E4%B9%8B%E5%9F%BA%E7%A1%80%E4%BD%B ...
- [转载] go get 拉取第三方包过慢、卡住解决方案
修改go env,选用国内的代理地址下载.
- NodeJs学习日报day9——操作数据库
const mysql = require('mysql') const db = mysql.createPool({ // 数据库的ip地址 host: 'localhost', user: 'r ...