【C++】基础

一、C++打印

#include <iostream>// 输入输出库(必须写)
using namespace std;// 简化代码

int main(int argc, const char * argv[]) {
    // insert code here...
    std::cout << "Hello, World!\n" << "Hello C++! 我正在用 XCode 学习~\n" ;
    
    //简化后
    cout << "Hello C++! 我正在用学习~" << endl;
    return EXIT_SUCCESS;
}

二、程序的基本结构

1.以#开头的预处理编译指令

2.命名空间namespace

3.主函数main()与其参数

4.在Xcode中运行参数的命令行

#include <iostream>// 输入输出库(必须写)

//全局引用
//using namespace std;

int main(int argc, const char * argv[]) {
    
    //局部引用
    using  std::cout;
    using  std::endl;
    
    cout << argc << endl;
    cout << argv[0] << endl;
    cout << argv[1] << endl;
    
    
    return EXIT_SUCCESS;
}

启动时传递的参数:Product -> Scheme ->Edit Scheme

    cout << argc << endl;

    cout << argv[0] << endl;

    cout << argv[1] << endl;

打印结果

2 ------>代表一共有2个字符串

/Users/chenli/Library/Developer/Xcode/DerivedData/Build/Products/Debug/cppstudy ------>代表默认索引为可执行项目的全路径

134152344 ------>代表在Edit Scheme添加的参数

三、输入输出与变量常量

1.输入

    //局部引用
    using  std::cout;
    using  std::endl;
    
    using std::cin;//引用输入

    int first,second;
    cout << "请输入第一个数字:" << endl;
    cin>>first;
    cout << "请输入第二个数字:" << endl;
    cin>>second;
    cout << "和为:"<<first+second << endl;

2.变量与常量

    int first,second;   //变量  数值可修改

    const int third=5;   //常量  数值不可修改

   常量一般全部大写用于区分

    const int THIRD=5; 

四、数据类型

1.整数类型

//有符号的整数类型
    short int s_num=0;
    long int l_num=0;
    //无符号的整数类型
    unsigned int u_num=0;
    // 简写
    unsigned u = 0;
    
    unsigned short int us_num=0;
    // 简写
    unsigned short us = 0;
    
    unsigned long int ul_num=0;
    // 简写
    unsigned long ul = 0;
    
    // 无符号超长整型
    unsigned long long int ull_num = 0;
    // 简写
    unsigned long long ull = 0;

2.字符类型

// 有符号字符(存字母、符号、数字字符)
    char ch = 'A';

    // 无符号字符
    unsigned char uch = 'B';

3.布尔类型

  // 真/假
    bool flag = true;
    bool is_ok = false;

4.浮点类型(小数)

    // 单精度浮点数
    float f_num = 3.14f;

    // 双精度浮点数(最常用)
    double d_num = 3.14159;

    // 长双精度浮点数
    long double ld_num = 3.1415926535;

5.字符串类型

#include <string>  // 必须包含头文件

int main() {
    // 正确写法
    std::string str = "Hello World";
    return 0;
}

//或

#include <string>
using namespace std;  // 引入std命名空间

int main() {
    // 可以直接使用string
    string str = "Hello World";
    return 0;
}

sizeof(变量名) 的使用:计算这个变量在内存中占用多少个字节(byte)

五、数组的声明、初始化、使用

1.数组声明语法

//类型 数组名[数组长度];

// 整型数组,能存 5 个 int
int arr[5];

// 浮点数组
float score[10];

// 字符数组
char name[20];

2、数组初始化

1. 声明时直接赋值

// 自动长度为 4
int arr[] = {10, 20, 30, 40};

// 指定长度 + 赋值
int arr[5] = {1, 2, 3, 4, 5};

// 部分赋值,剩下自动为 0
int arr[5] = {1, 2};   // {1,2,0,0,0}

2. 先声明,后赋值

int arr[3];
arr[0] = 11;
arr[1] = 22;
arr[2] = 33;

3.数组的使用

//数组名[下标]

int arr[4] = {10,20,30,40};

// 读取
cout << arr[0];   // 10
cout << arr[3];   // 40

// 修改
arr[1] = 99;      // arr 变成 {10,99,30,40}

越界访问会报错 / 崩溃

4.遍历数组(循环输出)

int arr[5] = {1,2,3,4,5};

for(int i=0; i<5; i++){
    cout << arr[i] << " ";
}

5.常用技巧:求数组长度

int arr[] = {10,20,30,40};

// 总字节数 / 单个元素字节数 = 元素个数
int len = sizeof(arr) / sizeof(arr[0]);

6.字符数组(字符串)

// 字符数组
char str[6] = {'h','e','l','l','o','\0'};

// 更常用
char str[] = "hello";

7.二维数组

// 2行3列
int arr[2][3] = {
    {1,2,3},
    {4,5,6}
};

// 访问
arr[0][1] = 99;

六、语句、表达式和运算符

1.表达式

//表达式 = 变量 + 常量 + 运算符
//特点:一定会算出一个值。

a + b
a = 10
a > 5
a++

2.语句

//语句 = 以分号;结尾的一行代码
//是程序执行的最小单位。

int a = 10;       // 声明语句
a = a + 1;        // 赋值语句
cout << a;        // 输出语句
if(a > 0){}       // 条件语句
for(;;){}         // 循环语句
return 0;         // 返回语句

3.表达式与语句的区分

  • 有值的是表达式
  • 能执行的是语句

4.运算符

1. 算术运算符

a + b    // 加
a - b    // 减
a * b    // 乘
a / b    // 除
a % b    // 取余(模运算,整数专用)

a++      // 后置自增(先用再加)
++a      // 前置自增(先加再用)
a--      // 后置自减
--a      // 前置自减

2. 赋值运算符

a = 10;     // 赋值
a += 5;     // a = a + 5
a -= 5;     // a = a - 5
a *= 5;     // a = a * 5
a /= 5;     // a = a / 5
a %= 5;     // a = a % 5

3. 关系运算符(比较,结果是 true/false)

a == b    // 等于
a != b    // 不等于
a > b     // 大于
a < b     // 小于
a >= b    // 大于等于
a <= b    // 小于等于

4. 逻辑运算符

!a        // 非(取反)
a && b    // 与(两边都真才真)
a || b    // 或(一边真就真)

5. 位运算符

a & b     // 按位与
a | b     // 按位或
a ^ b     // 按位异或
~a        // 按位取反
a << 1    // 左移
a >> 1    // 右移

6. 其他常用运算符

sizeof(a)       // 占字节大小
&a              // 取地址
*p              // 解引用
?:              // 三目运算符

三目运算符示例:

int max = (a > b) ? a : b;

5.运算符优先级(从高到低,记重点)

  1. () 括号
  2. ++ --(后置)
  3. ! ~
  4. * / %
  5. + -
  6. < > <= >=
  7. == !=
  8. &&
  9. ||
  10. = += -= 等赋值

记不住就加括号,最稳。

6.常见语句分类

1.声明语句

int a;
int arr[5];

2.表达式语句

a = 1 + 2;
a++;

3.选择语句

if(){}
if(){}else{}
switch(){}

4.循环语句

while(){}
do{}while();
for(;;){}

5.跳转语句

break;
continue;
return;
goto;
特殊:goto的使用

是 C++ 里的无条件跳转语句,可以直接跳到代码里你标记的位置执行。

//先给某一行打一个标签(名字 + 冒号)
//用 goto 标签名; 跳过去

goto 标签名;

标签名:
    要执行的代码;



例子1:

#include <iostream>
using namespace std;

int main() {
    cout << "1" << endl;

    // 直接跳到 FLAG 位置
    goto FLAG;

    cout << "2" << endl;  // 这一行不会执行

FLAG:
    cout << "3" << endl;

    return 0;
}

输出结果:

1
3

//常用场景:出错时统一退出

int main() {
    int a;
    cin >> a;

    if (a < 0) {
        goto END;  // 直接跳到结束
    }

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

END:
    cout << "程序结束" << endl;
    return 0;
}

//用 goto 实现循环(不推荐,但能看懂)

int i = 0;
LOOP:
cout << i << endl;
i++;
if (i < 5) goto LOOP;

6.空语句

;  // 只有一个分号

七、条件判断语句的使用

1.if 语句

1.if

if (条件) {
    条件成立时执行这里;
}

//示例
int score = 90;
if (score >= 60) {
    cout << "及格" << endl;
}

2.if … else

if (条件) {
    成立执行这里;
} else {
    不成立执行这里;
}

//示例:
if (score >= 60) {
    cout << "及格";
} else {
    cout << "不及格";
}

3. if … else if … else(多条件)

if (条件1) {
} else if (条件2) {
} else if (条件3) {
} else {
}

示例:
int score = 85;

if (score >= 90) {
    cout << "优秀";
} else if (score >= 80) {
    cout << "良好";
} else if (score >= 60) {
    cout << "及格";
} else {
    cout << "不及格";
}

4. 嵌套 if(if 里再套 if)

if (年龄 >= 18) {
    if (成绩 >= 60) {
        cout << "成年且及格";
    } else {
        cout << "成年但不及格";
    }
} else {
    cout << "未成年";
}

2.switch 语句

switch (变量) {
    case 值1:
        代码;
        break;
    case 值2:
        代码;
        break;
    default:
        都不满足时执行;
}

int week = 3;

switch (week) {
    case 1:
        cout << "周一";
        break;
    case 2:
        cout << "周二";
        break;
    case 3:
        cout << "周三";
        break;
    default:
        cout << "未知";
}

八、循环语句的使用

1. while 循环(先判断,再执行)

while (条件) {
    循环体代码;
}

int i = 1;
while (i <= 5) {
    cout << i << endl;
    i++;   // 必须更新,否则死循环
}

2. do while 循环(先执行一次,再判断)

do {
    循环体代码;
} while (条件);

int i = 1;
do {
    cout << i << endl;
    i++;
} while (i <= 5);

3. for 循环

for (初始化; 条件; 更新) {
    循环体;
}


for (int i = 1; i <= 5; i++) {
    cout << i << endl;
}

4. 循环控制语句

break

直接跳出整个循环,循环结束。

for (int i = 1; i <= 10; i++) {
    if (i == 6) break;
    cout << i << " ";  // 输出 1 2 3 4 5
}

continue

跳过本次循环剩下的语句,直接进入下一次循环

for (int i = 1; i <= 5; i++) {
    if (i == 3) continue;
    cout << i << " ";  // 输出 1 2 4 5
}

5. 嵌套循环(循环里套循环)

for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 3; j++) {
        cout << i << "," << j << "  ";
    }
    cout << endl;
}

九、函数

1.函数的结构(四要素)

返回值类型 函数名(参数列表) {
    函数体;
    return 返回值;
}
  • 返回值类型:函数执行完要返回什么类型的数据(int、double、void 等)
  • 函数名:见名知意
  • 参数列表:传给函数的数据
  • 函数体:具体做什么
  • return:把结果返回去(没有返回值用 void,可以不写 return)

2.最简单的函数

// 无返回值、无参数
void hello() {
    cout << "Hello" << endl;
}

调用:
hello();

3.带参数的函数

// 求两个数的和
int add(int a, int b) {
    return a + b;
}

调用:
int sum = add(3, 5);   // sum = 8

4.带返回值 vs 无返回值

1. 有返回值(int、double、char…)

必须用 return 送回一个结果。

int max(int a, int b) {
    return a > b ? a : b;
}

2. 无返回值(void)

不用 return,或者写 return;

void print(int x) {
    cout << x << endl;
}

4.函数声明

如果函数写在 main 后面,必须在 main 前面声明,否则报错。

// 函数声明
int add(int a, int b);

int main() {
    cout << add(2, 3);
}

// 函数定义
int add(int a, int b) {
    return a + b;
}

5.函数重载

函数名相同,参数不同,C++ 自动匹配。

int add(int a, int b);
double add(double a, double b);

调用:
add(1, 2);      // 调用 int 版本
add(1.1, 2.2);  // 调用 double 版本

6.值传递

把变量的值复制一份传给函数,函数内部修改,外面的变量不会变

void f(int x) {
    x = 100;
}

int main() {
    int a = 10;
    f(a);
    // a 还是 10
}

7.常用函数示例

1. 求最大值

int max(int a, int b) {
    if (a > b) return a;
    else return b;
}

2. 判断偶数

bool isEven(int n) {
    return n % 2 == 0;
}

3. 打印数组

void printArr(int arr[], int len) {
    for (int i = 0; i < len; i++) {
        cout << arr[i] << " ";
    }
}

十、指针和引用

1.指针

    1.定义语法

    指针就是存地址的变量

    • 普通变量:存数据
    • 指针变量:存内存地址
    类型 *指针名;
    
    示例:
    int a = 10;
    int *p = &a;   // p 指向 a 的地址
    
    // &a:取地址符号,获取变量 a 的地址
    // int *p:定义一个指向 int 类型的指针

    2.解引用 *

    通过指针拿到它指向的值:

    int a = 10;
    int *p = &a;
    
    cout << *p;   // 输出 10,*p 就是 a

    3.用指针修改值

    *p = 20;   // 等价于 a = 20

    4.指针和数组

    数组名本质就是常量指针

    int arr[3] = {1,2,3};
    int *p = arr;
    
    cout << *p;     // 1
    cout << *(p+1); // 2
    cout << *(p+2); // 3

    2.引用

    1.定义语法

    引用就是变量的别名,相当于给变量起外号。

    类型 &别名 = 原名;
    
    int a = 10;
    int &b = a;   // b 是 a 的引用
    
    b = 20;       // a 也变成 20
    
    
    特点:
    引用必须初始化
    一旦绑定,不能再改绑别的变量
    操作引用 = 操作原变量

    3.指针 vs 引用(超级重点对比)

    相同点

    • 都能间接操作原变量

    不同点

    1. 指针是变量,可空、可改指向

    int *p = nullptr;  // 可以为空
    p = &a;
    p = &b;            // 可以改指向

         2.引用是别名,不能为空,不能重绑

    int &r;     // 错误,必须初始化
    r = c;      // 不是改绑,是赋值

         3.指针用 * 取值,引用直接用

         4.指针可以做算术(p++),引用不行

    4.最常用场景:函数传参

    1. 指针做参数(地址传递)

    void change(int *p) {
        *p = 100;
    }
    
    int a = 10;
    change(&a);   // a 变成 100

    2. 引用做参数(推荐)

    void change(int &r) {
        r = 100;
    }
    
    int a = 10;
    change(a);    // a 变成 100

    好处:

    • 比指针简单安全
    • 不用写 *
    • 不会空指针

    十一、类、对象和构造函数

    1.类和对象

    1. 类(class)

    类就是自定义类型,把属性 + 行为打包在一起。

    • 属性:成员变量
    • 行为:成员函数
    class 类名 {
    public:
        // 成员变量(属性)
        // 成员函数(行为)
    };
    
    示例:
    
    class Student {
    public:
        // 属性
        string name;
        int age;
    
        // 行为
        void show() {
            cout << name << " " << age << endl;
        }
    };

    2. 对象(Object)

    对象就是类的具体实例。类是 “图纸”,对象是 “房子”。

    //定义对象
    Student s1;
    
    //使用对象:
    s1.name = "张三";
    s1.age = 18;
    s1.show();

    3. 访问权限

    • public:公开,外部可以访问
    • private:私有,只有类内部能访问(默认)
    • protected:继承用
    class A {
    private:
        int x;  // 外部不能直接用
    public:
        void setX(int v) { x = v; }
    };

    2.构造函数

    1. 什么是构造函数

    • 创建对象时自动调用
    • 用来初始化成员变量
    • 名字必须和类名完全一样
    • 没有返回值(连 void 都不用写)

    2. 语法

    类名() {
        // 初始化
    }
    
    示例:
    
    class Student {
    public:
        string name;
        int age;
    
        // 构造函数
        Student() {
            name = "无名";
            age = 0;
        }
    };
    
    创建对象时自动执行:
    
    Student s;
    // s.name 自动是 "无名"

    3. 带参数的构造函数

    Student(string n, int a) {
        name = n;
        age = a;
    }
    
    创建对象:
    
    Student s1("张三", 18);
    Student s2 = Student("李四", 19);

    4. 默认构造函数

    如果你一个构造函数都不写,编译器会自动给一个空的默认构造。但如果你写了任意构造函数,默认构造就消失

    3.析构函数

    • 名字:~类名()
    • 对象销毁时自动调用
    • 无参、无返回值
    • 用来释放资源(如指针)
    ~Student() {
        cout << "对象销毁" << endl;
    }

    4.this 指针

    每个成员函数里都有一个 this,代表当前对象自己

    void setName(string name) {
        this->name = name;
    }
    
    // this-> 用来区分成员变量和同名参数。

    5.完整示例

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Student {
    private:
        string name;
        int age;
    
    public:
        // 构造函数
        Student(string n, int a) {
            name = n;
            age = a;
            cout << "构造" << endl;
        }
    
        void show() {
            cout << name << " " << age << endl;
        }
    
        ~Student() {
            cout << "析构" << endl;
        }
    };
    
    int main() {
        Student s("小明", 18);
        s.show();
        return 0;
    }

    十二、结构体与联合体

    1.结构体(struct)

    1. 是什么

    多个不同类型的变量打包在一起,变成一个新类型。

    可以理解成:超级简单的类

    struct 结构体名 {
        成员1;
        成员2;
        ...
    };

    2. 定义结构体

    struct Student {
        int id;
        string name;
        int age;
    };

    3. 定义结构体变量(对象)

    Student s;

    4. 访问成员

    . 访问:

    s.id = 1001;
    s.name = "张三";
    s.age = 18;

    5. 指针访问结构体(->)

    Student *p = &s;
    p->id = 1002;
    p->name = "李四";

    6. struct 和 class 的区别

    • class 默认权限是 private
    • struct 默认权限是 public
    • 其他几乎一样(struct 也能写函数、构造函数)

    2.联合体(union)

    1. 是什么

    多个成员共用同一块内存空间

    同一时间,只能用其中一个成员

    union 联合体名 {
        成员1;
        成员2;
        ...
    };
    
    示例:
    
    union Data {
        int i;
        float f;
        char c;
    };

    2.特点

    • 所有成员从同一个地址开始
    • 大小 = 最大成员的大小
    • 改一个成员,其他成员的值会被覆盖
    Data d;
    d.i = 10;    // 用 int
    d.f = 3.14f; // 覆盖 int,现在只能用 float

    3.大小计算

    union Data {
        int i;    // 4
        float f;  // 4
        char c;   // 1
    };
    
    // sizeof(Data) = 4(取最大的 int/float)

    3.结构体 vs 联合体对比

    特性结构体 struct联合体 union
    内存使用各成员独立空间,依次排列所有成员共用同一块内存
    同时使用可以同时访问所有成员同一时间只能用一个成员
    大小所有成员大小之和(可能对齐)等于最大成员的大小
    赋值影响改一个不影响其他改一个会覆盖其他所有成员

    4.常用小例子

    结构体示例

    struct Point {
        int x;
        int y;
    };
    
    Point p = {10, 20};
    cout << p.x << " " << p.y;

    联合体示例(判断系统大小端)

    union Test {
        int a;
        char b;
    };
    
    Test t;
    t.a = 1;
    if (t.b == 1)
        cout << "小端";
    else
        cout << "大端";

    十三、文件的读写

    C++ 文件读写主要用文件流

    ifstream(读文件)、ofstream(写文件)、fstream(可读可写)。

    1.头文件

    #include <fstream>
    #include <iostream>
    #include <string>
    using namespace std;

    2.写文件

    1. 普通文本写入

    // 创建写文件对象
    ofstream ofs("test.txt");
    
    // 判断是否打开成功
    if (!ofs) {
        cout << "打开失败" << endl;
        return 0;
    }
    
    // 写入内容
    ofs << "Hello 文件" << endl;
    ofs << 123 << endl;
    
    // 关闭文件
    ofs.close();

    2. 追加写入(不清空原有内容)

    // 加 ios::app
    ofstream ofs("test.txt", ios::app);
    ofs << "追加一行" << endl;
    ofs.close();

    3.读文件(从文件读取)

    方式 1:按行读取(最常用)

    ifstream ifs("test.txt");
    
    if (!ifs) {
        cout << "打开失败" << endl;
        return 0;
    }
    
    string line;
    // 循环读每一行
    while (getline(ifs, line)) {
        cout << line << endl;
    }
    
    ifs.close();

    方式 2:按空格 / 换行读取(类似 cin)

    string s;
    int a;
    ifs >> s >> a;   // 自动按空白分割

    4.二进制读写

    // 写
    ofstream ofs("data.bin", ios::binary);
    int x = 100;
    ofs.write((char*)&x, sizeof(x));
    
    // 读
    ifstream ifs("data.bin", ios::binary);
    int y;
    ifs.read((char*)&y, sizeof(y));

    5.文件打开模式(ios::)

    ios::out      // 写(默认)
    ios::in       // 读(默认)
    ios::app      // 追加
    ios::binary   // 二进制
    ios::trunc    // 清空原有内容
    
    组合使用:
    fstream fs("a.txt", ios::in | ios::out);

    6.完整案例

    //
    //  main.cpp
    //  cppstudy
    //
    //  Created by 陈立 on 2026/4/21.
    //
    
    #include <fstream>
    #include <iostream>
    #include <string>
    // 加上这个头文件
    #include <filesystem>
    using namespace std;
    namespace fs = filesystem;
    
    // 先声明函数(告诉编译器有这个函数)
    void WriteFlie();
    void AddWrite();
    void ReadFile();
    
    int main(int argc, const char * argv[]) {
        
        WriteFlie();
        AddWrite();
        ReadFile();
        
        return EXIT_SUCCESS;
    }
    
     void WriteFlie(){
        // 创建写文件对象
        ofstream ofs("test.txt");
    
        // 判断是否打开成功
        if (!ofs) {
            cout << "打开失败" << endl;
            return;
        }
    
        // 写入内容
        ofs << "Hello 文件" << endl;
        ofs << 123 << endl;
    
        // 关闭文件
        ofs.close();
         
         // 直接输出完整路径
        cout << "文件已写入:" << fs::absolute("test.txt") << endl;
    }
    
    void AddWrite(){
        // 加 ios::app
        ofstream ofs("test.txt", ios::app);
        ofs << "追加一行" << endl;
        ofs.close();
        
    }
    
    void ReadFile(){
        ifstream ifs("test.txt");
    
        if (!ifs) {
            cout << "打开失败" << endl;
            return ;
        }
    
        string line;
        // 循环读每一行
        while (getline(ifs, line)) {
            cout << line << endl;
        }
    
        ifs.close();
        
    }
    

    输出写入文件目录:

    // 加上这个头文件
    #include <filesystem>
    namespace fs = filesystem;
    
    cout << "文件已写入:" << fs::absolute("test.txt") << endl;

    十四、模版template

    模板就是 **“代码的模具”,让一段代码通用多种数据类型 **,不用重复写 int、double、string 等版本。

    1.函数模板

    1. 语法

    template <typename T>   // 或 template <class T>
    返回值 函数名(T 参数) {
        // 使用 T 作为类型
    }
    
    template <> 声明这是模板
    T 是类型参数(可以随便起名:T/Type/Elem)
    typename 和 class 在这里完全一样

    2. 示例:通用交换函数

    template <typename T>
    void swap(T &a, T &b) {
        T temp = a;
        a = b;
        b = temp;
    }
    
    调用时自动适配类型:
    
    int a = 1, b = 2;
    swap(a, b);
    
    double x = 1.1, y = 2.2;
    swap(x, y);
    
    string s1 = "hello", s2 = "world";
    swap(s1, s2);

    3. 多个类型参数

    template <class T1, class T2>
    void print(T1 a, T2 b) {
        cout << a << " " << b << endl;
    }

    2.类模板

    1. 语法

    template <typename T>
    class 类名 {
        // 成员可以用 T
    };

    2. 示例:通用数组类

    template <class T>
    class MyArray {
    private:
        T arr[10];
    public:
        void set(int index, T val) {
            arr[index] = val;
        }
        T get(int index) {
            return arr[index];
        }
    };

    使用时必须指定类型

    MyArray<int> arr1;
    arr1.set(0, 10);
    
    MyArray<double> arr2;
    arr2.set(0, 3.14);

    3.模板的特点

    • 模板不是函数 / 类,是模具用到时才会根据类型生成具体代码

    • 类型必须能支持模板里的操作比如模板里用了 +,传入的类型就必须支持加法

    • 函数模板可以自动推导类型

    add(1, 2);    // 自动 T = int
    • 类模板不能自动推导,必须写 <>

    MyArray obj;      // 错误
    MyArray<int> obj; // 正确

    4.最常用场景

    • 通用算法:swapmaxminsort
    • 通用容器:vector<T>list<T>map<T1,T2>
    • 减少重复代码,一套代码支持所有类型

    示例 1:通用打印数组算法

    #include <iostream>
    using namespace std;
    
    // 通用打印算法
    template <typename T>
    void printArray(T arr[], int len) {
        for (int i = 0; i < len; i++) {
            cout << arr[i] << " ";
        }
        cout << endl;
    }
    
    int main() {
        // int数组
        int a[] = {1,2,3,4,5};
        printArray(a, 5);
    
        // double数组
        double b[] = {1.1, 2.2, 3.3};
        printArray(b, 3);
    
        // string数组
        string s[] = {"apple", "banana", "orange"};
        printArray(s, 3);
    }

    示例 2:通用求最大值算法

    template <typename T>
    T getMax(T a, T b) {
        return a > b ? a : b;
    }
    
    // 使用
    cout << getMax(10, 20) << endl;
    cout << getMax(3.14, 2.71) << endl;
    cout << getMax(string("zoo"), string("apple")) << endl;

    示例3:通用栈容器(最经典)

    #include <iostream>
    using namespace std;
    
    // 通用栈类模板
    template <typename T, int SIZE = 10>
    class MyStack {
    private:
        T arr[SIZE];
        int top;
    
    public:
        MyStack() {
            top = -1;
        }
    
        // 入栈
        void push(T val) {
            if (top >= SIZE - 1) return;
            arr[++top] = val;
        }
    
        // 出栈
        T pop() {
            if (top < 0) return T();
            return arr[top--];
        }
    
        // 判断空
        bool isEmpty() {
            return top == -1;
        }
    };
    
    int main() {
        // int栈
        MyStack<int> st1;
        st1.push(10);
        st1.push(20);
        cout << st1.pop() << endl; // 20
    
        // double栈
        MyStack<double> st2;
        st2.push(3.14);
        cout << st2.pop() << endl; // 3.14
    
        // string栈
        MyStack<string> st3;
        st3.push("hello");
        cout << st3.pop() << endl; // hello
    }

    示例4:STL 真实通用容器用法

    #include <vector>
    #include <list>
    #include <map>
    using namespace std;
    
    // 1. vector 动态数组
    vector<int> v1;
    vector<string> v2;
    
    // 2. list 链表
    list<int> lst;
    
    // 3. map 键值对
    map<int, string> m;
    m[1] = "张三";

    十五、异常处理(try、throw、catch)

    1.三个关键字

    1. try尝试执行可能出错的代码块

    2. throw抛出异常,相当于报警

    3. catch捕获并处理异常,相当于接警处理

    2.基本语法结构

    try {
        // 可能出错的代码
        // 出错时 throw 抛出异常
    }
    catch (异常类型1) {
        // 处理异常1
    }
    catch (异常类型2) {
        // 处理异常2
    }

    3.最简单示例:除零异常

    #include <iostream>
    using namespace std;
    
    int div(int a, int b) {
        if (b == 0) {
            // 抛出异常
            throw "除数不能为零!";
        }
        return a / b;
    }
    
    int main() {
        try {
            cout << div(10, 2) << endl;
            cout << div(10, 0) << endl; // 会出错
        }
        // 捕获 const char* 类型异常
        catch (const char* msg) {
            cout << "捕获异常:" << msg << endl;
        }
    
        cout << "程序继续正常运行~" << endl;
        return 0;
    }

    4.抛出多种类型异常

    可以 throw int、double、string、对象 等任意类型。

    void test(int x) {
        if (x < 0)
            throw -1;            // int 异常
        if (x == 0)
            throw 0.0;          // double 异常
        if (x > 100)
            throw "数值过大";   // const char*
    }
    
    捕获:
    
    try {
        test(120);
    }
    catch (int e) {
        cout << "int异常:" << e << endl;
    }
    catch (double e) {
        cout << "double异常:" << e << endl;
    }
    catch (const char* msg) {
        cout << "字符串异常:" << msg << endl;
    }

    5.捕获所有异常(万能 catch)

    不管抛什么类型都能接住:

    catch (...) {
        cout << "发生未知异常!" << endl;
    }

    ... 表示任意类型

    6.异常对象(常用)

    可以自定义异常类,更规范:

    class MyError {
    public:
        const char* msg;
        MyError(const char* m) : msg(m) {}
    };
    
    void test() {
        throw MyError("自定义异常");
    }
    
    // 捕获
    catch (MyError e) {
        cout << e.msg << endl;
    }

    7.栈解旋(重要机制)

    当异常抛出时:

    • 从 try 到 throw 之间创建的局部对象会自动析构
    • 不用手动释放,非常安全

    8.标准库异常(实际开发常用)

    C++ 自带一套异常类,都继承自 exception

    #include <stdexcept>

    常用:

    • runtime_error 运行时错误
    • out_of_range 越界
    • invalid_argument 参数无效
    • bad_alloc new 分配内存失败

    示例:

    throw runtime_error("运行出错了");
    
    catch (exception& e) {
        cout << e.what() << endl;
    }

    十六、函数指针

    函数指针就是:指向函数的指针变量

    本质:把函数当作数据,存它的地址,以后可以通过指针调用这个函数。

    1.函数指针怎么定义

    语法格式:

    返回值类型 (*指针名)(参数列表);

    例子:

    有一个函数:
    
    int add(int a, int b) {
        return a + b;
    }
    
    定义能指向它的指针:
    
    int (*p_func)(int, int);
    
    赋值:
    
    p_func = add;
    
    调用:
    
    int res = p_func(10, 20); // 等价于 add(10,20)
    
    
    完整例子:
    #include <iostream>
    using namespace std;
    
    int add(int a, int b) {
        return a + b;
    }
    
    int main() {
        // 1. 定义函数指针
        int (*p)(int, int);
    
        // 2. 指向函数
        p = add;
    
        // 3. 通过指针调用函数
        cout << p(2, 3) << endl;   // 输出 5
    
        return 0;
    }

    2.简化写法(typedef)

    函数指针写起来很长,用 typedef 简化:

    typedef int (*FuncPtr)(int, int);
    

    以后直接用:

    FuncPtr p = add;
    p(1, 2);

    3.函数指针做函数参数(最常用)

    用途:回调函数

    把一个函数传给另一个函数去执行。

    #include <iostream>
    using namespace std;
    
    int add(int a, int b) { return a + b; }
    int sub(int a, int b) { return a - b; }
    
    // 用函数指针当参数
    void calculate(int a, int b, int (*op)(int, int)) {
        cout << op(a, b) << endl;
    }
    
    int main() {
        calculate(10, 5, add); // 15
        calculate(10, 5, sub); // 5
        return 0;
    }

    这就是通用回调机制

    4.和普通指针的区别

    • 普通指针:指向数据(int、char、对象)
    • 函数指针:指向代码段里的函数地址
    • 不能做 p++ 这种算术运算(无意义)

    5.函数指针数组 + 菜单选择(经典实战案例)

    • 定义多个功能函数(增、删、改、查、退出)
    • 函数指针数组把它们存起来
    • 根据用户输入的菜单编号,直接调用对应函数
    #include <iostream>
    using namespace std;
    
    // 1. 定义各个功能函数
    void add() {
        cout << "执行:添加数据" << endl;
    }
    
    void del() {
        cout << "执行:删除数据" << endl;
    }
    
    void update() {
        cout << "执行:修改数据" << endl;
    }
    
    void query() {
        cout << "执行:查询数据" << endl;
    }
    
    void exitSys() {
        cout << "退出系统,再见!" << endl;
    }
    
    int main() {
        // 2. 定义函数指针数组
        void (*funcArr[])() = {
            add,     // 编号 0
            del,     // 编号 1
            update,  // 编号 2
            query,   // 编号 3
            exitSys  // 编号 4
        };
    
        int choice;
    
        while (true) {
            // 菜单
            cout << "===== 菜单 =====" << endl;
            cout << "0. 添加" << endl;
            cout << "1. 删除" << endl;
            cout << "2. 修改" << endl;
            cout << "3. 查询" << endl;
            cout << "4. 退出" << endl;
            cout << "请输入选择:";
            cin >> choice;
    
            if (choice < 0 || choice > 4) {
                cout << "输入错误,请重新输入!" << endl;
                continue;
            }
    
            // 3. 通过函数指针数组调用函数
            funcArr[choice]();
    
            cout << endl;
    
            // 选择4就退出循环
            if (choice == 4) break;
        }
    
        return 0;
    }

    关键点讲解

    1. 函数指针数组定义
    void (*funcArr[])();

    含义:

    • funcArr 是一个数组
    • 数组里每个元素是:函数指针
    • 这些指针指向:无返回值、无参数的函数
    2. 赋值
    void (*funcArr[])() = {add, del, update, query, exitSys};

    直接把函数名放进去,函数名就是地址。

    3. 调用
    funcArr[choice]();
    • funcArr[choice] 拿到对应函数指针
    • 后面加 () 就调用该函数
    带参数版本(扩展)

    如果函数需要参数,比如:

    void test(int a) { ... }

    函数指针数组写法:

    void (*funcArr2[])(int) = { test1, test2, test3 };
    
    // 调用
    funcArr2[0](100);

    十七、Lambda 表达式

    Lambda 就是就地定义的匿名函数,不用单独写函数名,常用于简单逻辑、回调、算法参数。

    1.基本语法

    [捕获列表] (参数列表) -> 返回值类型 { 函数体 };
    
    简化写法(很多地方可以省略):
    
    [](){};
    
    各部分说明:
    1. [捕获列表] —— 最重要
    用来访问外部变量,相当于传参。
    [] 空:不捕获任何外部变量
    [=] 值捕获:外部变量都复制一份(只读)
    [&] 引用捕获:直接用外部变量(可修改)
    [x, &y] 混合:x 值捕获,y 引用捕获
    [this] 在类里捕获当前对象
    2. (参数)
    和普通函数参数一样,可省略。
    3. -> 返回值
    可省略,编译器自动推导。只有函数体有多条 return 且类型不一致时才需要写。
    4. {} 函数体
    正常写代码。

    2.示例

    auto f = []{ cout << "Hello Lambda" << endl; };
    f();   // 调用

    带参数:

    auto add = [](int a, int b){ return a + b; };
    cout << add(1,2);

    3.捕获外部变量

    1. 值捕获 [=]

    int a = 10;
    auto f = [=]{ cout << a; };

    内部是副本,修改不影响外面。

    2. 引用捕获 [&]

    int a = 10;
    auto f = [&]{ a = 20; };
    f();
    // a 变成 20

    3. 混合捕获

    int x=1, y=2;
    auto f = [x, &y]{
        // x 只读
        y = 100;
    };

    4.配合算法(最常用场景)

    #include <vector>
    #include <algorithm>
    
    vector<int> v = {3,1,4,2};
    
    // 排序
    sort(v.begin(), v.end(), [](int a, int b){
        return a < b;
    });
    
    // 遍历
    for_each(v.begin(), v.end(), [](int x){
        cout << x << " ";
    });

    5.带 mutable(修改值捕获变量)

    值捕获默认只读,加 mutable 可改副本:

    int a = 10;
    auto f = [a]() mutable {
        a = 99;
    };

    外部 a 不变。

    6.和函数指针搭配

    Lambda 可隐式转为函数指针(无捕获时):

    void (*fp)(int) = [](int x){ cout << x; };
    fp(10);

    十八、封装、继承、多态

    1.封装

    把属性和方法打包成类,控制访问权限,对外隐藏实现。

    一句话:保护内部数据,只暴露必要接口

    1. 访问权限

    • public:公开,外部可访问
    • private:私有,只有类内部可访问(默认)
    • protected:保护,类内部 + 子类可访问

    2. 示例

    class Person {
    private:
        int age;  // 私有属性,外部不能直接访问
    public:
        void setAge(int a) { age = a; }
        int getAge() { return age; }
    };

    作用:

    • 安全,防止乱改
    • 便于维护
    • 接口清晰

    2.继承

    子类继承父类,拥有父类的属性和方法,实现代码复用。

    1. 语法

    class 子类 : 继承方式 父类 { ... };

    2. 三种继承方式

    • public 公有继承:父类 public/protected 权限不变
    • protected 保护继承:父类都变 protected
    • private 私有继承:父类都变 private

    3. 示例

    class Person {
    public:
        void eat() { cout << "吃饭" << endl; }
    };
    
    class Student : public Person {
    public:
        void study() { cout << "学习" << endl; }
    };
    
    使用:
    
    Student s;
    s.eat();   // 继承自父类
    s.study(); // 自己的方法

    4. 继承中的构造

    • 先构造父类
    • 再构造子类析构顺序相反

    3.多态

    同一个接口,不同对象表现出不同行为。

    前提条件

    1. 有继承关系
    2. 子类 ** 重写(override)** 父类虚函数
    3. 父类指针 / 引用指向子类对象

    1. 虚函数

    在父类函数前加 virtual

    virtual void speak() { ... }

    2. 示例

    class Animal {
    public:
        virtual void speak() {
            cout << "动物叫" << endl;
        }
    };
    
    class Dog : public Animal {
    public:
        void speak() override {
            cout << "汪汪汪" << endl;
        }
    };
    
    class Cat : public Animal {
    public:
        void speak() override {
            cout << "喵喵喵" << endl;
        }
    };

    3. 多态使用

    void doSpeak(Animal &animal) {
        animal.speak();
    }
    
    int main() {
        Dog d;
        Cat c;
        doSpeak(d); // 输出汪汪汪
        doSpeak(c); // 输出喵喵喵
    }

    这就是多态:传入不同对象,执行不同逻辑。

    4. 纯虚函数 & 抽象类

    virtual void speak() = 0;

    包含纯虚函数的类叫抽象类,不能实例化,必须由子类实现纯虚函数。

    5. 虚析构

    父类指针删除子类对象时,为了调用子类析构,父类析构要加 virtual

    virtual ~Animal() {}

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值