C++总结笔记

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++; // ++操作 这是因为定义的数组,申请的是连续的内存空间,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值