C++ Primer Plus(第6版):第四章 复合类型

请关注我!我将后续上传视频讲解!(保姆级程度)


一、数组 --- Array

数组是同类型数据的连续存储结构 ,通过 "类型+数组名+长度" 声明,元素通过下标访问(从0开始),核心特点是"连续存储、同类型、固定长度(静态联编)"。

1.1 数组的声明与初始化

  • C++ 数组初始化方式对照表

    初始化方式 语法示例 说明
    传统初始化(带= int arr1[5] = {1,2,3,4,5}; 初始化所有元素,长度必须≥初始化值数量
    部分初始化 int arr2[5] = {1,2}; 未初始化元素默认为0
    省略长度(编译器推断) int arr3[] = {1,2,3}; 长度由初始化值数量决定(此处为3)
    C++11列表初始化(无= int arr4[5]{1,2,3,4,5}; 禁止缩窄转换(如int arr{3.14}报错)
    全部初始化为0 int arr5[5] = {0};int arr5[5]{}; 第一个元素为0,其余默认0
  • 代码示例

    cpp 复制代码
    #include <iostream>
    using namespace std;
    
    int main() {
        // 1. 基本声明与初始化
        int scores[5] = {90, 85, 95, 88, 92};  // 传统初始化
        double weights[] = {65.5, 72.3, 58.9}; // 省略长度(编译器推断为3)
        char chars[4]{'a', 'b', 'c', 'd'};     // C++11列表初始化
    
        // 2. 部分初始化与默认值
        int nums[5] = {1, 2};  // 未初始化元素默认为0 → [1,2,0,0,0]
        int zeros[3]{};        // 全部初始化为0 → [0,0,0]
    
        // 3. 访问数组元素(下标从0开始)
        cout << "scores[0] = " << scores[0] << endl;  // 90
        cout << "weights[2] = " << weights[2] << endl;// 58.9
    
        // 4. 遍历数组(结合for循环)
        cout << "chars数组元素:";
        for (int i = 0; i < 4; ++i) {
            cout << chars[i] << " ";  // 输出:a b c d
        }
        cout << endl;
    
        // 5. 数组长度计算(sizeof(数组名)/sizeof(元素类型))
        int arr_len = sizeof(scores) / sizeof(int);
        cout << "scores数组长度:" << arr_len << endl;  // 5
    
        return 0;
    }

1.2 数组的核心特性

  • 固定长度 :数组长度在编译时确定(静态联编),无法在运行时修改(如int n=5; int arr[n];报错,需用动态数组解决);
  • 连续存储 :元素在内存中连续排列,下标访问本质是"数组首地址+偏移量"(如arr[i]等价于*(arr+i));
  • 无边界检查 :编译器不检查下标是否越界(如arr[10]在长度为5的数组中访问,可能导致内存损坏)。

1.3 结合 cincoutconst

  • cin输入数组元素,cout输出数组

    cpp 复制代码
    #include <iostream>
    using namespace std;
    
    int main() {
        const int LEN = 3;  // 第三章const:限定数组长度为常量
        int arr[LEN];
    
        // 第二章cin:输入数组元素
        cout << "请输入" << LEN << "个整数:";
        for (int i = 0; i < LEN; ++i) {
            cin >> arr[i];  // 逐个输入元素
        }
    
        // 第二章cout:输出数组元素
        cout << "你输入的数组:";
        for (int i = 0; i < LEN; ++i) {
            cout << arr[i] << " ";  // 输出:如输入1 2 3 → 1 2 3
        }
        cout << endl;
    
        return 0;
    }
  • const限定"只读数组"(元素不可修改)

    cpp 复制代码
    #include <iostream>
    using namespace std;
    
    int main() {
        // 第三章const:限定数组元素只读(初始化后不可修改)
        const int months[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
    
        // 第二章cout:输出只读数组
        cout << "1月天数:" << months[0] << endl;  // 31
        cout << "2月天数:" << months[1] << endl;  // 28
    
        // months[1] = 29;  // 错误:const数组元素不可修改
        return 0;
    }

二、字符串 --- String

2.1 C-风格字符串

本质是"以\0(ASCII码0)结尾的字符数组",\0是字符串结束标志,否则会导致"内存越界访问"。

  • 声明与初始化

    cpp 复制代码
    #include <iostream>
    #include <cstring>  // 字符串处理函数(strlen、strcpy等)
    using namespace std;
    
    int main() {
        // 1. 直接用字符串常量初始化(自动添加\0)
        char str1[10] = "hello";  // 存储:'h','e','l','l','o','\0',0,0,0,0
        char str2[] = "world";    // 长度由编译器推断(6:5个字符+1个\0)
    
        // 2. 手动添加\0(必须)
        char str3[6] = {'h','i','\0'};  // 正确:显式添加\0
        // char str4[2] = {'h','i'};   // 错误:无\0,不是合法C-风格字符串
    
        // 3. 字符串长度计算(strlen:不包含\0;sizeof:包含所有字符)
        cout << "str1长度(strlen):" << strlen(str1) << endl;  // 5(不含\0)
        cout << "str1占用内存(sizeof):" << sizeof(str1) << endl;  // 10(数组总长度)
    
        // 4. 字符串拼接(strcat:目标数组必须足够大)
        char str5[20] = "Hello, ";
        strcat(str5, str2);  // 拼接str2到str5 → "Hello, world"
        cout << "拼接后str5:" << str5 << endl;
    
        // 5. 字符串复制(strcpy:避免目标数组溢出)
        char str6[10];
        strcpy(str6, "copy");  // 复制"copy"到str6 → "copy\0"
        cout << "str6:" << str6 << endl;
    
        return 0;
    }
  • cin/cout处理C-风格字符串

    cpp 复制代码
    #include <iostream>
    #include <cstring>
    using namespace std;
    
    int main() {
        char name[20];
        char greeting[50] = "Hello, ";
    
        // 第二章cin:输入字符串(遇空格/换行结束)
        cout << "请输入你的名字:";
        cin >> name;  // 如输入"Zhang San",仅读取"Zhang"(空格截断)
    
        // 字符串拼接+第二章cout输出
        strcat(greeting, name);
        cout << greeting << "!" << endl;  // 输出:Hello, Zhang!
    
        // 解决空格问题:用cin.getline()读取整行(第二章知识点)
        char full_name[30];
        cin.ignore();  // 忽略上一次cin遗留的换行符
        cout << "请输入你的全名:";
        cin.getline(full_name, 30);  // 读取整行(含空格)
        cout << "全名:" << full_name << endl;  // 如输入"Zhang San",完整读取
    
        return 0;
    }

2.2 C++ string 类(推荐)

string类是C++标准库提供的字符串类型,封装了字符数组的操作,支持"自动内存管理、赋值、拼接"等便捷操作,无需手动处理\0

  • string 类的基本用法

    • 声明与初始化
      string类的初始化方式灵活,支持多种语法,涵盖传统C++和C++11新增的列表初始化。

      初始化方式 语法示例 说明
      默认初始化 string str; 创建空字符串,长度为0,内存占用为默认(如16字节)
      字符串常量初始化 string str = "hello"; 用C-风格字符串初始化,自动添加\0(底层维护)
      构造函数初始化 string str("world"); 显式调用构造函数,效果与string str = "world"一致
      列表初始化(C++11) string str{"C++11"}; 支持空列表初始化(string str{};),等价于默认初始化
      截取子串初始化 string str("abcdef", 2, 3); 从索引2开始,截取3个字符("cde")
      重复字符初始化 string str(5, 'a'); 创建包含5个'a'的字符串("aaaaa")
      拷贝初始化 string str1 = str2; / string str1(str2); 用已有的string对象拷贝初始化新对象
    • 核心成员函数
      string类的成员函数覆盖字符串的常见操作,以下是最常用的函数。

      成员函数 功能描述 示例(string str = "hello world";
      size() / length() 获取字符串长度(不含底层\0 str.size() → 11
      empty() 判断字符串是否为空(长度为0返回true str.empty()false
      clear() 清空字符串(长度变为0,内存可能保留) str.clear();str.empty()true
      c_str() 转换为C-风格字符串(返回const char* cout << str.c_str(); → 输出"hello world"
      substr(pos, len) 截取子串:从pos开始,长度为lenlen省略则取到末尾) str.substr(6, 5) → "world"
      find(sub, pos) pos开始查找子串sub,返回首次出现的索引(未找到返回string::npos str.find("world") → 6
      replace(pos, len, sub) pos开始,替换len个字符为sub str.replace(5, 1, "-") → "hello-world"
      push_back(ch) 在字符串末尾追加单个字符 str.push_back('!') → "hello world!"
      append(str) 在字符串末尾追加另一个stringchar* str.append("!!!") → "hello world!!!"
    • 运算符重载
      string类重载了多种运算符,语法与基本类型一致,降低使用成本。

      运算符 功能描述 示例
      = 赋值:将右侧字符串赋值给左侧string对象 string str1; str1 = "test"; → str1 = "test"
      + 拼接:将两个字符串合并为一个新string对象 string str = "a" + "b"; → 错误(需至少一个string);string str = string("a") + "b"; → "ab"
      += 追加:将右侧字符串追加到左侧string末尾 string str = "a"; str += "b"; → "ab"
      == / != 比较:按字符ASCII码逐位比较,区分大小写 "abc" == "Abc"false"abc" != "abd"true
      < / > / <= / >= 大小比较:按字典序(ASCII码顺序) "apple" < "banana"true('a' < 'b')
      [] 下标访问:获取/修改指定索引的字符(无边界检查) string str = "abc"; str[1] = 'x'; → "axc"
      at(pos) 安全访问:获取/修改指定索引的字符(有边界检查,越界抛out_of_range异常) str.at(1) = 'y'; → "ayc";str.at(10) → 抛异常
    • 注意事项

      • 头文件依赖 :使用string类必须包含<string>头文件,且string位于std名称空间(需加using namespace std;或显式写std::string)。

        错误示例: #include <cstring>(仅用于C-风格字符串,不包含string类)。

      • 与C-风格字符串的转换
        stringchar* 通过c_str()成员函数,返回const char*(不可修改,若string被修改,返回的指针可能失效)。
        char*string 直接赋值(string str = "hello";)或通过构造函数(string str("world");),string会自动处理\0

      • 空字符串判断 :推荐用empty()成员函数(效率更高),而非size() == 0(两者效果一致,但empty()是专门优化的接口)。

      • 下标访问的边界问题

        1. []运算符无边界检查,若访问索引超出size()-1,会导致未定义行为(如内存损坏)。
        2. 安全访问用at(pos),越界时会抛出std::out_of_range异常,便于调试。
      • string的长度与容量
        size() / length() 返回字符串的实际字符数(不含底层\0)。
        capacity() 返回string当前分配的内存可存储的字符数(大于等于size()),扩容时capacity()会翻倍(减少内存分配次数)。

        若需释放多余内存,可调用shrink_to_fit()(C++11),但该函数仅为"建议",编译器可能忽略。

      • 字符串拼接的陷阱

        直接用"a" + "b"会报错,因为两个操作数都是char*类型,C++不会自动转换为string;需显式将其中一个转为string(如string("a") + "b")。

  • string 类的代码示例

    • 基本初始化与常用操作

      cpp 复制代码
      #include <iostream>
      #include <string>  // 必须包含string头文件
      using namespace std;  // 引入std名称空间,避免频繁写std::string
      
      int main() {
          // 1. 多种初始化方式
          string str1;                  // 默认初始化:空字符串
          string str2 = "hello";        // 字符串常量初始化
          string str3("world");         // 构造函数初始化
          string str4{"C++11 list"};    // C++11列表初始化
          string str5(3, '!');          // 重复字符初始化:"!!!"
          string str6 = str2 + " " + str3;  // 拼接初始化:"hello world"
      
          // 2. 输出字符串(直接用cout,无需处理\0)
          cout << "str2: " << str2 << endl;    // 输出:str2: hello
          cout << "str6: " << str6 << endl;    // 输出:str6: hello world
          cout << "str5: " << str5 << endl;    // 输出:str5: !!!
      
          // 3. 核心成员函数使用
          cout << "\n--- 核心成员函数 ---" << endl;
          cout << "str6长度(size()): " << str6.size() << endl;  // 输出:11
          cout << "str1是否为空(empty()): " << boolalpha << str1.empty() << endl;  // 输出:true
          cout << "str6的子串(substr(6,5)): " << str6.substr(6, 5) << endl;  // 输出:world
          cout << "str6中查找\"world\"(find()): " << str6.find("world") << endl;  // 输出:6
      
          // 4. 运算符重载使用
          cout << "\n--- 运算符重载 ---" << endl;
          str2 += " C++";  // 追加字符串:str2 = "hello C++"
          cout << "str2追加后: " << str2 << endl;  // 输出:str2追加后: hello C++
      
          string str7 = str2;  // 赋值运算符:str7 = "hello C++"
          if (str2 == str7) {  // 比较运算符:判断是否相等
              cout << "str2 == str7" << endl;  // 输出:str2 == str7
          }
      
          str7[6] = 'c';  // 下标访问:修改字符(str7 = "hello c++")
          cout << "str7修改后: " << str7 << endl;  // 输出:str7修改后: hello c++
      
          // 5. 清空与重新赋值
          cout << "\n--- 清空与重新赋值 ---" << endl;
          str1 = "I'm a new string";  // 空字符串赋值
          cout << "str1赋值后: " << str1 << endl;  // 输出:str1赋值后: I'm a new string
      
          str1.clear();  // 清空字符串
          cout << "str1清空后是否为空: " << str1.empty() << endl;  // 输出:true
      
          return 0;
      }
    • 与C-风格字符串的转换

      cpp 复制代码
      #include <iostream>
      #include <string>
      #include <cstring>  // 用于C-风格字符串函数(如strlen)
      using namespace std;
      
      int main() {
          // 1. C-风格字符串转string(直接赋值/构造)
          const char* c_str = "C-style string";  // C-风格字符串(const char*)
          string str1 = c_str;                   // 直接赋值转换
          string str2(c_str, 7);                 // 截取前7个字符:"C-style"
      
          cout << "str1(C-风格转string): " << str1 << endl;  // 输出:C-style string
          cout << "str2(截取C-风格字符串): " << str2 << endl;  // 输出:C-style
      
          // 2. string转C-风格字符串(通过c_str())
          string str3 = "Convert to C-style";
          const char* converted_c_str = str3.c_str();  // 必须用const char*接收
      
          cout << "\nstr3转C-风格字符串: " << converted_c_str << endl;  // 输出:Convert to C-style
          cout << "C-风格字符串长度(strlen): " << strlen(converted_c_str) << endl;  // 输出:18
      
          // 注意:c_str()返回的指针有效期与str3绑定,若str3被修改,指针可能失效
          str3 += "!!!";  // 修改str3,converted_c_str可能变为野指针
          // cout << converted_c_str << endl;  // 危险:可能输出乱码或崩溃,需重新调用c_str()
          converted_c_str = str3.c_str();  // 重新获取指针
          cout << "str3修改后转C-风格: " << converted_c_str << endl;  // 输出:Convert to C-style!!!
      
          return 0;
      }
    • 安全访问与异常处理

      cpp 复制代码
      #include <iostream>
      #include <string>
      #include <stdexcept>  // 用于捕获out_of_range异常
      using namespace std;
      
      int main() {
          string str = "hello world";
      
          // 1. 下标访问(无边界检查,越界会导致未定义行为)
          cout << "str[4](合法下标): " << str[4] << endl;  // 输出:o
          // cout << str[20] << endl;  // 危险:下标越界,可能崩溃或输出乱码
      
          // 2. at()访问(有边界检查,越界抛异常)
          cout << "\n--- at()安全访问 ---" << endl;
          try {
              cout << "str.at(4)(合法下标): " << str.at(4) << endl;  // 输出:o
              str.at(20) = 'x';  // 越界,抛出out_of_range异常
          } catch (const out_of_range& e) {
              // 捕获异常并处理
              cout << "访问异常: " << e.what() << endl;  
              // 输出:访问异常: basic_string::at: __n (which is 20) >= this->size() (which is 11)
          }
      
          // 3. 空字符串的安全判断
          string empty_str;
          if (empty_str.empty()) {  // 推荐用empty(),而非size() == 0
              cout << "\nempty_str is empty" << endl;  // 输出:empty_str is empty
          }
      
          return 0;
      }
    • 字符串的查找与替换

      cpp 复制代码
      #include <iostream>
      #include <string>
      using namespace std;
      
      int main() {
          string str = "I like C++, C++ is easy to learn!";
      
          // 1. 查找子串(find())
          cout << "--- 查找子串 ---" << endl;
          size_t pos1 = str.find("C++");  // 首次出现"C++"的索引
          if (pos1 != string::npos) {  // 判断是否找到(string::npos表示未找到)
              cout << "首次找到\"C++\"的索引: " << pos1 << endl;  // 输出:7
          }
      
          size_t pos2 = str.find("C++", pos1 + 1);  // 从pos1+1开始查找下一个"C++"
          if (pos2 != string::npos) {
              cout << "第二次找到\"C++\"的索引: " << pos2 << endl;  // 输出:11
          }
      
          // 2. 替换子串(replace())
          cout << "\n--- 替换子串 ---" << endl;
          string new_str = "C++20";
          // 从pos1开始,替换3个字符为new_str("C++"→"C++20")
          str.replace(pos1, 3, new_str);
          cout << "替换后str: " << str << endl;  // 输出:I like C++20, C++ is easy to learn!
      
          // 3. 查找单个字符(find_first_of())
          size_t pos3 = str.find_first_of("aeiou");  // 首次出现元音字母的索引
          if (pos3 != string::npos) {
              cout << "\n首次出现元音字母的索引: " << pos3 << endl;  // 输出:1
              cout << "首次出现的元音字母: " << str[pos3] << endl;  // 输出:i
          }
      
          return 0;
      }
  • const限定只读stringcin/cout

    cpp 复制代码
    #include <iostream>
    #include <string>
    using namespace std;
    
    int main() {
        // 第三章const:限定string对象只读(不可修改内容)
        const string fixed_str = "固定字符串";
        // fixed_str = "修改";  // 错误:const限定不可修改
    
        // 第二章cin:输入string(支持空格,无需指定长度)
        string user_input;
        cout << "请输入一段文本(含空格):";
        getline(cin, user_input);  // 读取整行(比cin.getline()更简洁)
    
        // 第二章cout:输出拼接结果
        string result = fixed_str + " → " + user_input;
        cout << "结果:" << result << endl;  // 如输入"test" → 固定字符串 → test
    
        return 0;
    }

三、结构 --- Structure

结构是不同类型数据的组合 ,通过struct关键字定义,可存储多个不同类型的成员(如整型、字符串、指针等),核心特点是 "封装不同类型数据,形成自定义复合类型"

3.1 结构的声明与使用

  • 基本语法: 定义结构类型→创建结构变量→访问成员

    cpp 复制代码
    #include <iostream>
    #include <string>
    using namespace std;
    
    // 1. 定义结构类型(外部声明,所有函数可使用)
    struct Student {
        // 成员变量(不同类型)
        string name;  // string类成员
        int age;      // 整型成员
        double score; // 浮点型成员
    };  // 注意分号
    
    int main() {
        // 2. 创建结构变量并初始化
        Student s1 = {"Zhang San", 18, 92.5};  // 传统初始化
        Student s2{"Li Si", 19, 88.0};          // C++11列表初始化
        Student s3;                             // 默认初始化(成员为默认值)
    
        // 3. 访问成员(用成员运算符.)
        s3.name = "Wang Wu";
        s3.age = 17;
        s3.score = 95.0;
    
        // 4. 输出结构成员(结合第二章cout)
        cout << "学生1:" << endl;
        cout << "姓名:" << s1.name << endl;
        cout << "年龄:" << s1.age << endl;
        cout << "成绩:" << s1.score << endl;
    
        // 5. 结构赋值(成员逐一赋值)
        Student s4 = s1;  // s4的name、age、score与s1完全相同
        cout << "学生4姓名:" << s4.name << endl;  // Zhang San
    
        return 0;
    }
  • 结构数组(数组元素为结构)

    cpp 复制代码
    #include <iostream>
    #include <string>
    using namespace std;
    
    struct Book {
        string title;
        string author;
        double price;
    };
    
    int main() {
        // 定义结构数组并初始化
        Book books[2] = {
            {"C++ Primer Plus", "Stephen Prata", 89.0},
            {"Effective C++", "Scott Meyers", 78.5}
        };
    
        // 遍历结构数组(结合for循环与cout)
        cout << "图书列表:" << endl;
        for (int i = 0; i < 2; ++i) {
            cout << "书名:" << books[i].title << endl;
            cout << "作者:" << books[i].author << endl;
            cout << "价格:" << books[i].price << "元" << endl;
            cout << "------------------------" << endl;
        }
    
        return 0;
    }

3.2 const限定只读结构与cin输入

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

struct Product {
    string name;
    double price;
    int stock;
};

// 函数参数:const限定结构(防止函数修改结构成员)
void printProduct(const Product& p) {  // 引用传递(避免拷贝,后续章节讲解)
    cout << "商品名称:" << p.name << endl;
    cout << "商品价格:" << p.price << "元" << endl;
    cout << "库存数量:" << p.stock << endl;
    // p.price = 100;  // 错误:const限定不可修改
}

int main() {
    Product p;
    // 第二章cin:输入结构成员
    cout << "请输入商品信息:" << endl;
    cout << "名称:";
    cin >> p.name;
    cout << "价格:";
    cin >> p.price;
    cout << "库存:";
    cin >> p.stock;

    // 调用函数输出(const结构作为参数)
    printProduct(p);

    return 0;
}

四、共用体 --- Union

共用体是同一内存空间存储不同类型数据的结构 ,所有成员共享同一块内存,同一时间只能存储一个成员的值,核心特点是 "节省内存(适合多类型数据不同时使用的场景)"

4.1 共用体的声明与使用

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 定义共用体(成员共享内存)
union Data {
    int i;      // 4字节
    double d;   // 8字节
    char c;     // 1字节
};  // 共用体大小为最大成员的大小(此处为8字节)

int main() {
    Data data;

    // 1. 存储int类型
    data.i = 100;
    cout << "存储int:" << data.i << endl;  // 100
    // cout << data.d << endl;  // 错误:当前存储的是int,读取double为垃圾值

    // 2. 存储double类型(覆盖int的值)
    data.d = 3.14;
    cout << "存储double:" << data.d << endl;  // 3.14
    // cout << data.i << endl;  // 错误:当前存储的是double,读取int为垃圾值

    // 3. 存储char类型(覆盖double的值)
    data.c = 'A';
    cout << "存储char:" << data.c << endl;  // A
    cout << "char的ASCII码:" << static_cast<int>(data.c) << endl;  // 65

    // 4. 共用体大小(等于最大成员的大小)
    cout << "共用体Data大小:" << sizeof(data) << endl;  // 8(double的大小)

    return 0;
}

4.2 共用体结合enum(枚举)与cout

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 枚举:标识共用体当前存储的类型(后续"枚举"章节讲解)
enum DataType { INT, DOUBLE, CHAR };

union Value {
    int i;
    double d;
    char c;
};

struct TaggedValue {
    DataType type;  // 标识当前存储的类型
    Value val;      // 共用体:存储实际值
};

int main() {
    TaggedValue tv;

    // 存储int类型
    tv.type = INT;
    tv.val.i = 200;

    // 存储double类型
    // tv.type = DOUBLE;
    // tv.val.d = 5.67;

    // 根据类型输出(结合第二章cout)
    switch (tv.type) {
        case INT:
            cout << "值(int):" << tv.val.i << endl;  // 200
            break;
        case DOUBLE:
            cout << "值(double):" << tv.val.d << endl;
            break;
        case CHAR:
            cout << "值(char):" << tv.val.c << endl;
            break;
        default:
            cout << "未知类型" << endl;
    }

    return 0;
}

五、枚举 --- Enumeration

枚举是用户定义的整数类型 ,通过enum关键字定义一组"符号常量"(枚举量),核心作用是 "用有意义的名称替代魔法数字,提升代码可读性"

5.1 枚举的声明与使用

  • 基本枚举(不限定作用域)

    cpp 复制代码
    #include <iostream>
    using namespace std;
    
    // 1. 定义枚举类型(枚举量默认值:0,1,2,3,4)
    enum Color { RED, GREEN, BLUE, YELLOW, PURPLE };
    
    // 2. 显式指定枚举量值
    enum Weekday { MON = 1, TUE, WED, THU, FRI, SAT, SUN };  // TUE=2,依次递增
    
    int main() {
        // 3. 创建枚举变量
        Color c = BLUE;
        Weekday day = MON;
    
        // 4. 输出枚举量(本质是整数,结合第二章cout)
        cout << "BLUE对应的整数:" << BLUE << endl;  // 2(默认值)
        cout << "当前颜色:" << c << endl;          // 2(BLUE)
        cout << "周一对应的整数:" << day << endl;   // 1(MON=1)
    
        // 5. 枚举量比较(本质是整数比较)
        if (c == BLUE) {
            cout << "当前颜色是蓝色" << endl;  // 成立
        }
    
        // 6. 枚举量赋值(只能赋值枚举量,不能直接赋值整数)
        c = RED;  // 正确
        // c = 0;   // 错误:需强制转换
        c = static_cast<Color>(0);  // 正确:强制转换(0对应RED)
    
        return 0;
    }
  • C++11作用域内枚举(enum class,推荐)

    避免枚举量名称冲突(如不同枚举中的RED冲突),需显式指定作用域。

    cpp 复制代码
    #include <iostream>
    using namespace std;
    
    // 作用域内枚举:枚举量需通过"枚举名::枚举量"访问
    enum class Fruit { APPLE, BANANA, ORANGE };
    enum class Color { RED, GREEN, BLUE };  // 与Fruit的RED不冲突
    
    int main() {
        Fruit f = Fruit::APPLE;
        Color c = Color::RED;
    
        // 输出:需强制转换为整数(作用域内枚举不自动隐式转换)
        cout << "Fruit::APPLE = " << static_cast<int>(f) << endl;  // 0
        cout << "Color::RED = " << static_cast<int>(c) << endl;    // 0
    
        // 错误:不同枚举的枚举量不可比较
        // if (f == c) {}  // 编译器报错
    
        return 0;
    }

5.2 const与枚举结合(固定枚举映射)

cpp 复制代码
#include <iostream>
using namespace std;

// 枚举:月份
enum Month { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };

int main() {
    // 第三章const:限定每月天数为常量(与枚举映射)
    const int DAYS_IN_MONTH[] = {31,28,31,30,31,30,31,31,30,31,30,31};

    // 输入月份(第二章cin)
    int input_month;
    cout << "请输入月份(1-12):";
    cin >> input_month;

    // 转换为枚举(需检查合法性)
    if (input_month < 1 || input_month > 12) {
        cout << "无效月份" << endl;
        return 1;
    }
    Month m = static_cast<Month>(input_month - 1);  // 1→JAN(0)

    // 输出天数(第二章cout)
    cout << input_month << "月有" << DAYS_IN_MONTH[m] << "天" << endl;  // 如输入2→28

    return 0;
}

六、指针 --- Pointer

指针是存储内存地址的变量 ,核心作用是 "间接访问内存、实现动态内存管理、传递大型数据(避免拷贝)" ,语法关键是"*(解引用,访问地址对应的值)"和"&(取地址,获取变量的内存地址)"。

6.1 指针的声明与基本操作

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    // 1. 声明指针(类型需与指向的变量一致)
    int num = 100;
    int* p_num = &num;  // p_num存储num的地址(&是取地址运算符)
    double pi = 3.14;
    double* p_pi = &pi;

    // 2. 解引用指针(*p访问地址对应的值)
    cout << "num的值:" << num << endl;          // 100
    cout << "p_num指向的值:" << *p_num << endl;  // 100(解引用)
    cout << "num的地址:" << &num << endl;       // 如0x0065fd40
    cout << "p_num存储的地址:" << p_num << endl;// 如0x0065fd40

    // 3. 修改指针指向的值(通过解引用)
    *p_num = 200;
    cout << "修改后num的值:" << num << endl;    // 200

    // 4. 指针算术(指针+1,偏移量为指向类型的大小)
    int arr[3] = {1,2,3};
    int* p_arr = arr;  // 数组名本质是首元素地址(arr = &arr[0])
    cout << "p_arr指向的值:" << *p_arr << endl;  // 1(arr[0])
    p_arr++;  // 指针+1,偏移4字节(int大小),指向arr[1]
    cout << "p_arr++后指向的值:" << *p_arr << endl;  // 2(arr[1])

    return 0;
}

6.2 动态内存管理 --- new/delete

指针的核心应用之一是"动态内存分配"------在运行时(而非编译时)分配内存,通过new申请内存,delete释放内存,避免内存泄漏。

  • 动态分配基本类型

    cpp 复制代码
    #include <iostream>
    using namespace std;
    
    int main() {
        // 1. 动态分配int(new返回内存地址,赋值给指针)
        int* p_int = new int;  // 申请4字节内存
        *p_int = 50;           // 给动态内存赋值
        cout << "动态int的值:" << *p_int << endl;  // 50
    
        // 2. 动态分配double
        double* p_double = new double(3.14);  // 初始化:分配内存并赋值3.14
        cout << "动态double的值:" << *p_double << endl;  // 3.14
    
        // 3. 释放动态内存(必须!避免内存泄漏)
        delete p_int;    // 释放int内存
        delete p_double; // 释放double内存
        p_int = nullptr; // 指针置空(避免野指针)
        p_double = nullptr;
    
        return 0;
    }
  • 动态分配数组(new[]/delete[]

    cpp 复制代码
    #include <iostream>
    using namespace std;
    
    int main() {
        int n;
        // 第二章cin:输入数组长度(运行时确定,动态联编)
        cout << "请输入数组长度:";
        cin >> n;
    
        // 动态分配数组(new[],返回首元素地址)
        int* p_dyn_arr = new int[n];  // 分配n个int的连续内存
    
        // 给动态数组赋值
        for (int i = 0; i < n; ++i) {
            p_dyn_arr[i] = i * 10;  // 如n=3 → [0,10,20]
        }
    
        // 第二章cout:输出动态数组
        cout << "动态数组元素:";
        for (int i = 0; i < n; ++i) {
            cout << p_dyn_arr[i] << " ";  // 0 10 20
        }
        cout << endl;
    
        // 释放动态数组(必须用delete[],不能用delete)
        delete[] p_dyn_arr;
        p_dyn_arr = nullptr;
    
        return 0;
    }

6.3 const 限定指针与动态结构

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

struct Person {
    string name;
    int age;
};

int main() {
    // 1. 动态分配结构(new+结构)
    Person* p_person = new Person;

    // 第二章cin:输入结构成员(指针访问成员用->)
    cout << "请输入姓名:";
    cin >> p_person->name;  // 指针访问成员:->(替代.)
    cout << "请输入年龄:";
    cin >> p_person->age;

    // 2. const限定指针(两种场景)
    const Person* p_const_person = p_person;  // 指向的结构不可修改(成员只读)
    // p_const_person->age = 20;  // 错误:成员不可修改

    Person* const const_p_person = p_person;  // 指针本身不可修改(地址固定)
    // const_p_person = new Person;  // 错误:指针地址不可修改

    // 第二章cout:输出结构成员
    cout << "姓名:" << p_const_person->name << endl;  // 如"Zhang"
    cout << "年龄:" << p_const_person->age << endl;   // 如18

    // 释放动态结构
    delete p_person;
    p_person = nullptr;

    return 0;
}

七、数组的替代品 --- vector/array

C++标准库提供了更安全、灵活的数组替代品:vector(动态数组,堆内存)和array(固定长度数组,栈内存),解决了原生数组"无边界检查、长度固定"的问题。

7.1 模板类vector(动态数组)

cpp 复制代码
#include <iostream>
#include <vector>  // 必须包含vector头文件
using namespace std;

int main() {
    // 1. 声明与初始化
    vector<int> vec1;                  // 空vector(长度0)
    vector<int> vec2(5, 0);           // 长度5,所有元素为0 → [0,0,0,0,0]
    vector<int> vec3 = {1,2,3,4,5};   // 传统初始化
    vector<int> vec4{6,7,8};          // C++11列表初始化

    // 2. 动态添加元素(自动扩容)
    vec1.push_back(10);  // vec1 → [10]
    vec1.push_back(20);  // vec1 → [10,20]

    // 3. 访问元素(下标或at(),at()有边界检查)
    cout << "vec3[2] = " << vec3[2] << endl;    // 3(无边界检查)
    cout << "vec3.at(2) = " << vec3.at(2) << endl;  // 3(有边界检查,越界抛异常)

    // 4. 长度与遍历
    cout << "vec1长度:" << vec1.size() << endl;  // 2
    cout << "vec1元素:";
    for (int i = 0; i < vec1.size(); ++i) {
        cout << vec1[i] << " ";  // 10 20
    }
    cout << endl;

    return 0;
}

7.2 模板类array(C++11,固定长度)

cpp 复制代码
#include <iostream>
#include <array>  // 必须包含array头文件
using namespace std;

int main() {
    // 1. 声明与初始化(长度必须是常量,静态联编)
    array<int, 5> arr1 = {1,2,3,4,5};  // 传统初始化
    array<int, 5> arr2{6,7,8,9,10};    // C++11列表初始化
    array<int, 5> arr3;                // 默认初始化(元素为随机值)

    // 2. 赋值(同类型同长度array可直接赋值)
    arr3 = arr1;  // arr3 → [1,2,3,4,5]

    // 3. 访问与遍历(支持下标、at(),有边界检查)
    cout << "arr2.at(3) = " << arr2.at(3) << endl;  // 9
    cout << "arr3元素:";
    for (auto val : arr3) {  // 范围for循环(C++11)
        cout << val << " ";  // 1 2 3 4 5
    }
    cout << endl;

    // 4. 长度(固定,size()为编译时常量)
    cout << "arr1长度:" << arr1.size() << endl;  // 5

    return 0;
}

7.3 vector结合cin/coutconst

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

// 函数参数:const限定vector(只读,防止修改)
void printVector(const vector<double>& vec) {
    cout << "vector元素:";
    for (double val : vec) {
        cout << val << " ";  // 第二章cout输出
    }
    cout << endl;
}

int main() {
    vector<double> vec;
    double input;

    // 第二章cin:输入元素,直到输入非数字
    cout << "请输入若干小数(输入非数字结束):";
    while (cin >> input) {
        vec.push_back(input);  // 动态添加元素
    }

    // 输出vector(调用函数,const参数)
    printVector(vec);

    // 计算平均值(结合第三章算术运算)
    double sum = 0.0;
    for (double val : vec) {
        sum += val;
    }
    if (!vec.empty()) {
        cout << "平均值:" << sum / vec.size() << endl;  // 如输入1.1 2.2 → 1.65
    }

    return 0;
}
相关推荐
MediaTea8 小时前
Python:接口隔离原则(ISP)
开发语言·网络·python·接口隔离原则
承渊政道8 小时前
C++学习之旅【C++内存管理、模板初阶以及STL简介】
c++·学习·visual studio
Clarence Liu8 小时前
Golang slice 深度原理与面试指南
开发语言·后端·golang
遇印记8 小时前
java期末复习(构造方法和成员方法,重写和重载)
java·开发语言·学习
郝学胜-神的一滴8 小时前
使用Qt OpenGL开发俄罗斯方块:从零到一实现经典游戏
c++·qt·程序人生·游戏·设计模式·系统架构·图形渲染
weixin_307779138 小时前
Jenkins声明式流水线权威指南:从Model API基础到高级实践
开发语言·ci/cd·自动化·jenkins·etl
Aevget8 小时前
DevExtreme JS & ASP.NET Core v25.2预览 - DataGrid/TreeList全新升级
开发语言·javascript·asp.net·界面控件·ui开发·devextreme
海涛高软8 小时前
Qt菜单项切换主界面
开发语言·qt
码界奇点8 小时前
基于Golang与Vue3的全栈博客系统设计与实现
开发语言·后端·golang·车载系统·毕业设计·源代码管理