1. 简介
1、面向对象程序设计
面向对象的四大特性
1)封装
2)继承
3)多态
4)抽象
2、标准库
标准C++由三个部分组成
1)核心语言:提供了所有的构件块
2)C++标准库:提供了大量的函数
3)标准模版库(STL):提供了大量的方法
2. 基本语法
2.1 变量
变量的作用:变量存在的意义,方便操作内存中的数据。
(1)内存中的数据可以通过 内存地址编号 得到
(2)给内存起名称(变量)方便管理这段内存。
变量的定义、变量的声明:
变量可以多次声明,但是只能被定义一次。
可以使用extern关键字在任何地方声明一个变量。
#include <iostream>
using namespace std;
// 变量声明
extern int a;
extern double b;
int main()
{
// 变量定义
int a;
double b;
// 实际初始化
a = 10;
b = 20.0;
cout << a << endl;
cout << b << endl;
return 0;
}
2.2 常量
常量:记录程序中不可修改的数据。
定义常量的两种方式
(1)#define 常量名 常量值 // 通过宏进行定义。
(2)const 数据类型 常量名 = 常量值 // const修饰的变量
2.3 关键字
关键字(标识符)
标识符命令规则
(1)不能是关键字
(2)只能是数字、字母、下划线
(3)第一个字符必须为字母或者下划线
(4)标识符中字母区分大小写
2.4 数据类型
数据类型存在的意义:给变量分配一个合适的内存空间。
2.4.1 类型修饰符
signed
unsigned
short
long
long long
2.4.2 基本内置类型
bool 1
char 1
int 4
float 4
double 8
void 无类型
wchar_t 宽字符型
注意⚠️:使用endl,在每一行后会插入一个换行符,<<用于向屏幕传递多个值
1、 整型
2、 实型(浮点型)
默认情况下输出一个小数,会显示6位有效数字。
(1)单精度float 4。
float f1 = 3.14f; // 这里的**f代表前面的float**。默认情况下编译器把小数当成双精度,会去进行类型转换
(2)双精度double 8
3、 字符型
‘a’ 对应的ASCII码是97
‘A’ 对应的ASCII码是65
char 1
字符型变量并不是把字符本身存储到内存中存储,而是将对应的ASCII编码放入到存储单元中。
作用:用于表示单个字符
注意:
1)使用单引号括起来
2)单引号里只能有一个字符,不可以是字符串。
查看字符型变量对应的ASCII码
ch = 'a';
cout << (int)ch << endl; // 97
4、 字符串类型(使用双引号)
C语言风格的字符串:char 变量名[] = “字符串值”
C++语言风格的字符串:string 变量名 = “字符串值”。需要一个头文件 #include
5、 布尔类型bool
bool 1
6、 转义字符
\n
\t
\v
\c
\r
2.4.3 typedef 声明
为已有的类型(int/bool/char等)定义一个新的名称。
typedef type newname;
例如:typedef int feet; // feet是int类型的一个新名称
2.4.4 枚举(enumeration)类型
一个变量只有几种可能的值,则可以定义为枚举类型。(即将变量的值一一列举出来,变量的值只能在列举出的值的范围内)
例子:
enum color { red, green = 5, blue } c;
c = blue;
说明:创建枚举类型color的变量c,并为c赋值为blue。
2.4.5 类型转换
将一个类型转换为另一个类型
四种类型转换:
(1)静态转换(Static Cast)
(2)动态转换(Dynamic Cast)
(3)常量转换(Constant Cast)
(4)重新解释转换(Reinterpret Cast)
确实,C++提供了四种类型转换运算符,每种都有其特定的用途。下面简要介绍这四种转换以及它们的应用场景:
1. 静态转换(Static Cast):
-
语法:static_cast<TargetType>(expression)
-
使用场景:它是最通用的转换形式,用于将一种类型转换为另一种类型,例如基本数据类型的转换(如 float 转 int),指向基类的指针或引用转为指向派生类的指针或引用等。
-
注意:此转换在编译时进行检查,但不执行运行时类型检查。
float f = 3.14;
int i = static_cast<int>(f); // i will be 3
2. 动态转换(Dynamic Cast):
-
语法:dynamic_cast<TargetType>(expression)
-
使用场景:主要用于处理多态时的指针和引用转换。它在运行时进行检查以确保所请求的转换是安全的和有效的。
-
注意:此转换通常用于将基类指针转换为派生类指针。
class Base {};
class Derived : public Base {};
Base *b = new Derived;
Derived *d = dynamic_cast<Derived *>(b); // Safe conversion
3. 常量转换(Constant Cast):
-
语法:const_cast<TargetType>(expression)
-
使用场景:用于修改表达式的常量性,例如从const类型转换为非const类型或从volatile类型转换为非volatile类型。
-
注意:这只改变类型的const或volatile属性,并不改变实际数据。
const int ci = 10;
int *nonConstIntPtr = const_cast<int *>(&ci);
4. 重新解释转换(Reinterpret Cast):
-
语法:reinterpret_cast(expression)
-
使用场景:对任何指针或整数类型进行低级别的强制类型转换。它的用途通常是将一种类型的指针转换为另一种类型的指针。
-
注意:此转换可能是不安全的,因为它不进行任何类型检查或转换,只是简单地告诉编译器将数据重新解释为另一种类型。
int i = 10;
void* ptr = reinterpret_cast<void *>(&i);
请注意,尽管C++提供了这些强制转换运算符,但最佳实践是尽量避免使用它们,除非在某些必要的情况下。当可能的时候,总是首选C++提供的安全和自动的类型转换。
5. 数据输入/输出
cin >> a;
cout >> "Hello" >> endl;
2.5 运算符
2.5.1 算数运算符
两个整数相除结果依然是整数
只有整型(int)变量可以进行取模运算
++/–区别:
++ 前置递增/后置递增
– 前置递减/后置递减
前置后置的区别:
前置,先进行变量的加1减1操作,然后进行表达式的计算。
后置,先进性表达式的计算,然后进行变量的加1减1操作。
2.5.2 关系运算符
2.5.3 逻辑运算符
&& 与
|| 或
! 非
注意⚠️:在C++中除了0都为真。
2.5.4 位运算符
位运算符作用于位,并逐位执行操作。
&、|、^(按位与运算符、按位或运算符、按位异或运算符)
~、<<、>>(取反运算符、二进制左移运算符、二进制右移运算符)
2.5.5 杂项运算符
1)sizeof:sizeof运算符 返回变量的大小
2)Condition ? X : Y 条件运算符(三目运算符)
3).(点)和->(箭头):成员运算符用于引用类、结构和共用体的成员。【访问结构的成员时使用点运算符,通过指针访问结构的成员时,使用箭头运算符】
4)Cast:强制转换运算符
5)&:指针运算符& 返回变量的地址
6)*:指针运算符* 指向一个变量
3. 程序流程结构
顺序结构
选择结构
循环结构
3.1 选择结构
1.1 三目运算符
表达式1 ? 表达式2 : 表达式3
1.2 if
1.3 switch… case…
3.2 循环结构
3.2.1 while
#include <iostream>
using namespace std;
// time系统时间头文件
#include <ctime>
int main(){
// 1、生成一个随机数
// 添加随机数种子 作用利用当前系统时间生成随机数,防止每次随机数一样的问题出现(伪随机数的出现)
srand((unsigned int)time(NULL));
int num = rand() % 100 + 1; // 这样生成的是伪随机数。需要添加随机数种子,进行生成可以防止生成伪随机数。
while (1)
{
int val = 0;
cin >> val;
if (val > num)
{
cout << "猜测的数字过大" << endl;
}
else if (val < num)
{
cout << "猜测的数字过小" << endl;
}
else if (val == num)
{
cout << "恭喜您!猜对了" << endl;
// 猜对之后退出猜测游戏。退出当前循环。
break;
}
}
return 0;
}
3.2.2 do…while
do…while与while的区别是do…while会先执行一次循环语句,再判断循环条件。
#include <iostream>
using namespace std;
int main(){
// 输出0~9数字
int num = 0;
do
{
cout << num << endl; // 先执行一次循环输出0,再判断循环条件。
num ++;
}
while (num < 10);
return 0;
}
案例 水仙花数
水仙花数是三位数。每个位上的数字求三次幂之后,相加等于这个三位数。
例如:1^3 + 5^3 + 3^3 = 153
利用do…while求出所有3位数中的水仙花数。
#include <iostream>
using namespace std;
/*
水仙花数
水仙花数是三位数。每个位上的数字求三次幂之后,相加等于这个三位数。
例如:1^3 + 5^3 + 3^3 = 153
利用do...while求出所有3位数中的水仙花数。
*/
int main(){
/*
1、打印所有三位数
2、从所有三位数中找到水仙花数
*/
int num = 100;
do
{
// 2.1 获取三位数的个位、十位、百位上的数字。
int a = 0;
int b = 0;
int c = 0;
a = num % 10; // 获取三位数的个位上数字
b = num / 10 % 10; // 获取三位数的十位上数字。整数相除得到的也是整数。
c = num / 100; // 获取三位数的百位上数字
// 2.2 判断是否是水仙花数,是则输出这个数字。
if ((a * a * a + b * b * b + c * c * c)==num)
{
cout << num << endl;
}
num++;
} while (num < 1000);
return 0;
}
3.2.3 for循环
案例 敲桌子
#include<iostream>
using namespace std;
/*
敲桌子
0~100数字。个位、十位有7,或者是7的倍数,敲桌子。
如果不是,输出这个数字。
*/
int main(){
// 1、输出0~100的这些数字。
for (int i = 0; i <= 100; i++)
{
// 2、找出特殊数字,输出“敲桌子”
if (i % 7 == 0 || i % 10 == 7 || i / 10 == 7)
{
cout << "敲桌子" << endl;
}
else
{
cout << i << endl;
}
}
return 0;
}
3.2.4 嵌套循环
实例 打印乘法口诀表
#include <iostream>
using namespace std;
int main(){
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= i; j++)
{
int eq = i * j;
cout << j << " * " << i << " = " << eq << " ";
}
cout << endl;
}
return 0;
}
3.3 跳转语句(break/continue)
3.3.1 break语句
作用:跳出整个选择结构或者整个循环结构
break使用的时机
(1)、出现在switch循环中。作用是终止case并跳出switch
(2)、出现在for循环中。作用是跳出当前循环
(3)、出现在嵌套循环语句中。跳出最近的内部循环语句
案例 嵌套循环 switch语句 break
#include <iostream>
using namespace std;
int main(){
cout << "请选择难易程度" << endl;
cout << "1、简单程度" << endl;
cout << "2、中等程度" << endl;
cout << "3、困难程度" << endl;
int select = 0;
cin >> select;
switch (select)
{
case 1:
std::cout << "简单" << std::endl;
break;
case 2:
std::cout << "中等" << std::endl;
break;
case 3:
std::cout << "困难" << std::endl;
break;
default:
break;
}
return 0;
}
案例 break的作用
```c++
#include <iostream>
using namespace std;
int main(){
for (int i = 1; i <= 10; i++)
{
for (int j = 1; j <= 10; j++)
{
if (j == 5)
{
break;
}
cout << " * ";
}
cout << endl;
}
return 0;
}
3.3.2 continue
跳出循环中的本次循环,不再向下执行本次循环,执行下一次循环。
#include<iostream>
using namespace std;
int main(){
for (int i = 0; i <= 100; i++)
{
if (i % 2 == 0) // 对2取模为0表示这个数是偶数
{
continue; // 跳出本次循环,进行下一次的循环
}
cout << i << endl;
}
return 0;
}
3.3.3 goto
跳转到另一部分代码进行执行。
4. 一维数组和指针
数组名含义:指向数据首地址的指针
4.1 指针的算数运算
& 取地址符:& 获得是一个变量的地址,返回的是指向这个变量的指针。
#include <iostream>
using namespace std;
int main(){
char a;
double b;
cout << "a的地址是" << (long long)&a << endl; // 获得指向该变量的指针(存储的是该变量的首地址)
cout << "a的地址+1值是" << (long long)(&a + 1) << endl; //
cout << "b的地址值是" << (long long)&b << endl; // 首地址
cout << "b的地址+1值是" << (long long)(&b + 1)<< endl; // 首地址
return 0;
}
/*
a的地址是6128757771
a的地址+1值是6128757772
b的地址值是6128757760
b的地址+1值是6128757768
*/
4.2 数组的地址
#include <iostream>
using namespace std;
int main(){
int arr[5];
cout << "arr的首地址" << (long long)&arr << endl;
cout << "a[0]的地址是" << (long long)&arr[0] << endl;
cout << "a[1]的地址是" << (long long)&arr[1] << endl;
cout << "a[2]的地址是" << (long long)&arr[2] << endl;
cout << "a[3]的地址是" << (long long)&arr[3] << endl;
cout << "a[4]的地址是" << (long long)&arr[4] << endl;
cout << "a[0] + 1的值是" << (long long)(&arr[0] + 1) << endl;
// 对指针进行算数运算
int * p = arr;
cout << " " << p + 0 << endl;
cout << " " << p + 1 << endl;
cout << "p + 0的值" << (long long)(p + 0) << endl;
cout << "p + 1的值" << (long long)(p + 1) << endl;
cout << "p + 2的值" << (long long)(p + 2) << endl;
return 0;
}
/*
arr的首地址6165801972
a[0]的地址是6165801972
a[1]的地址是6165801976
a[2]的地址是6165801980
a[3]的地址是6165801984
a[4]的地址是6165801988
a[0] + 1的值是6165801976
0x16f82abf4
0x16f82abf8
p + 0的值6165801972
p + 1的值6165801976
p + 2的值6165801980
*/
注意的点:
1、 数组的特点:
1)存放具有相同数据类型的元素;
2)数组元素存放在连续的内存空间中。
2、 一维数组的三种定义方式
3、 通过下标访问数组中的数据
4、 利用循环的方式输出数组中的元素。
int arr[5] = {val1, val2, val3};
输出后的arr[3] = 0 , arr[4] = 0。
5、 一维数组名的两种用途:
1)统计数组在内存中的长度;
sizeof(arr);
2)获取数组在内存中的首地址。
cout << arr << endl;
练习1: 寻找小猪体重最大的值
#include <iostream>
using namespace std;
int main(){
int max = 0;
int arr[5] = {300, 350, 500, 400, 250};
for (int i = 0; i < 5; i++)
{
if (arr[i] > max)
{
max = arr[i];
}
}
cout << "最重的小猪体重为:" << max << endl;
return 0;
}
练习2: 数组元素逆置(首尾元素互换)
#include<iostream>
using namespace std;
int main(){
int arr[] = {21, 78, 11, 54, 12};
int start = 0;
int end = sizeof(arr) / sizeof(arr[0]) - 1;
while (start < end)
{
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
for (int i = 0; i < 5; i++)
{
cout << arr[i] << endl;
}
return 0;
}
4.3 数组和指针的关系
数组名相当于指向数组首元素的指针
#include <iostream>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
// 使用数组名来访问数组元素
std::cout << "Array elements using array name:" << std::endl;
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
// 使用指针来访问数组元素
int *ptr = arr; // 将数组名赋给指针
std::cout << "Array elements using a pointer:" << std::endl;
for (int i = 0; i < 5; i++) {
std::cout << ptr[i] << " ";
}
std::cout << std::endl;
return 0;
}
/*
数组名可以视为指向数组首元素的指针。
*/
5. 二维数组
5.1 二维数组的定义/输出
#include<iostream>
using namespace std;
int main(){
// 二维数组的定义
// 1、第一种定义方式
int arr01[2][3] = {
{1, 4, 2}, {7, 10, 3}};
// 2、第二种定义方式
int arr02[2][3];
arr02[0][0] = 1;
arr02[0][1] = 4;
// 3、第三种定义方式
int arr03[2][3] = {1, 4, 2, 7, 10, 3};
// 4、第四种定义方式
int arr04[][3] = {1, 4, 2, 7, 10, 3}; // 可省略行数,但是列数不能省略
// 二维数组的输出
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
cout << arr01[i][j] << " ";
}
}
}
5.2 二维数组的数组名的作用
1)二维数组占用的内存空间
sizeof(arr)
2)二维数组的首地址
cout << arr << endl;
#include<iostream>
using namespace std;
int main(){
int arr[2][3] = {
{1, 4, 2}, {7, 10, 3}};
// 数组名的作用
// 1. 数组占用的内存空间
cout << "二维数组占用的内存空间:" << sizeof(arr) << endl;
cout << "二维数组第一行元素占用的内存空间:" << sizeof(arr[0]) << endl;
cout << "二维数组第一个元素占用的内存空间:" << sizeof(arr[0][0]) << endl;
cout << "二维数组的行数:" << sizeof(arr) / sizeof(arr[0]) << endl;
// 2. 查看二维数组的首地址
cout << "二维数组的首地址 " << (long)arr << endl;
cout << "二维数组的第一行的首地址 " << (long)arr[0] << endl;
cout << "二维数组的第二行的首地址 " << (long)arr[1] << endl;
cout << "二维数组的第一个元素的首地址 " << (long)&arr[0][0] << endl; // 查看一个数的首地址需要加取地址符&
cout << "二维数组的第二个元素的首地址 " << (long)&arr[0][1] << endl;
return 0;
}
实例 成绩的统计
#include<iostream>
using namespace std;
#include<string>
int main(){
// 考试成绩的统计
int scores[3][3] =
{
{100, 100, 100},
{90, 50, 100},
{60, 70, 80}
};
string names[3] = {"张三", "李四", "王五"};
for (int i = 0; i < 3; i++)
{
int sum = 0;
for (int j = 0; j < 3; j++)
{
sum += scores[i][j];
}
cout << names[i] << "的总分:" << sum << endl;
}
return 0;
}
6. 函数
作用:将一段经常使用的代码进行封装,减少重复代码
6.1 函数的定义/调用
实现一个加法函数,功能:传入两个整型数据,计算数据相加的结果,并且返回。
// 定义加法函数
// num1和num2为形参
int add(int num1, int num2) // 返回值类型 函数名(参数列表){函数体 return表达式}
{
int sum = num1 + num2;
return sum;
}
#include<iostream>
using namespace std;
int main(){
int a = 7;
int b = 8;
// a,b为实参
int sum = add(a, b);
cout << sum << endl;
return 0;
}
6.2 值传递
值传递就是函数调用时实参将数值传入给形参
值传递时,如果形参发生变化,并不影响实参
#include<iostream>
using namespace std;
// 函数之“值传递”
void swap(int num1, int num2){
cout << "交换前的值";
cout << "num1的值:" << num1 << endl;
cout << "num2的值:" << num2 << endl;
cout << "交换后的值";
int temp = num1;
num1 = num2;
num2 = temp;
cout << "num1的值:" << num1 << endl;
cout << "num2的值:" << num2 << endl;
return; // 当函数返回值类型为void时, 可以不写,也可这样写
}
int main(){
int a = 10;
int b = 22;
swap(a, b);
cout << "值传递时,如果形参发生任何改变,并不影响实参" << endl;
cout << "a的值:" << a << endl;
cout << "b的值:" << b << endl;
return 0;
}
6.3 函数的样式
1. 无参无返
2. 无参有返
3. 有参无返
4. 有参有返
6.4 函数的声明
可以将函数往主函数后面写,提前使用函数声明告诉编译器这个函数是存在的。
#include <iostream>
using namespace std;
// 函数声明
// 提前告诉编译器函数的存在,可以利用函数的声明
int max(int num1, int num2);
int main(){
int a = 10;
int b = 20;
cout << max(a, b);
return 0;
}
int max(int num1, int num2)
{
return num1 > num2 ? num1 : num2; // 三目运算符
}
6.5 函数分文件编写
作用:让代码结构更加清晰
函数分文件编写步骤:
1)创建.h头文件
2)创建.cpp源文件
3)在.h头文件中写函数的声明
4)在.cpp源文件中写函数的定义
swap.h
#include <iostream>
using namespace std;
void swap(int a, int b);
swap.cpp
#include “swap.h”
void swap(int a, int b)
{
int tmp = a;
int a = b;
int b = tmp;
cout << “a = ” << a << endl;
cout << “b = ” << b << endl;
}
func.cpp
#include “swap.h”
int main()
{
int a = 10;
int b = 12;
swap(a, b);
return 0;
}
7. 指针
指针的作用:通过指针间接访问内存
指针存储的是内存地址
内存编号从0开始记录,一般使用十六进制数字表示
利用指针变量保存地址
7.1 指针变量的定义/使用
#include <iostream>
using namespace std;
int main()
{
// 1、指针的定义
int a = 10;
// 指针定义的语法:数据类型 *指针变量名
int *p;
// 让指针指向变量a的地址
p = &a;
cout << "a的地址为:" << &a << endl;
cout << "指针p为:" << p << endl;
// 2、使用指针
// 通过解引用的方式找到指针指向的内存
// 指针前加 * 代表解引用,找到指针指向的内存中的数据
*p = 1000;
cout << "a = " << a << endl;
cout << "*p = " << *p << endl;
return 0;
}
/*
a的地址为:0x16b3b2ba8
指针p为:0x16b3b2ba8
a = 1000
*p = 1000
*/
7.2 指针占用的内存空间
指针也是一种数据类型,占用内存空间多大?(指针这种数据类型占用的内存大小)
在32位操作系统下占用:4个字节
在64位操作系统下占用:8个字节
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int *p = &a;
// 查看指针数据类型占用的内存空间(注意在64/32位操作系统下,占用内存空间不同)
cout << "sizeof(int *) = " << sizeof(int *) << endl;
cout << "sizeof(double *) = " << sizeof(double *) << endl;
return 0;
}
7.3 空指针和野指针
7.3.1 空指针
空指针:指针变量指向内存编号为0的空间
用途:初始化指针变量 int *p = NULL;
注意:空指针指向的内存不能访问
// 指针变量p指向内存地址编号为0的空间
**int *p = NULL;**
// 访问空指针报错
// 内存编号0~255为系统占用内存,不允许用户访问
7.3.2 野指针/悬空指针
野指针:指针变量指向非法内存空间。不确定指针具体指向。
悬空指针:指针变量最初指向的内存已经被释放的指针。
注意:野指针/空指针都不是我们申请内存空间,因此不要访问。
7.4 const修饰指针
const修饰指针的3种情况:
1. char *const cp;
const修饰的cp在里面,所以cp指向的地址是不能改变的,但它所指向的内容是可以改变的;指针常量
2. char const *pc1;
*pc1被const所修饰,也就是指针所指向的对象,所以它指向的对象是不能改变的,但是它指向的地址是可以改变的;常量指针
3. const * const ptr
特点:指针指向不可以修改,值也不可以修改
7.5 指针和数组
作用:指针访问数组元素
注意:ptr++; // ++操作 这是因为定义的数组,申请的是连续的内存空间,