C++ Primer 第2章:变量和基本类型

C++ Primer 第2章:变量和基本类型


2.1 基本内置类型

2.1.1 算术类型

C++ 定义了一套包括算术类型空类型在内的基本数据类型。

复制代码
算术类型分类:
┌─────────────────────────────────────────────┐
│ 整型(含字符和布尔类型)                      │
│  bool / char / wchar_t / char16_t / char32_t │
│  short / int / long / long long              │
├─────────────────────────────────────────────┤
│ 浮点型                                       │
│  float / double / long double                │
└─────────────────────────────────────────────┘

算术类型的大小(典型64位系统):

类型 含义 最小尺寸
bool 布尔类型 未定义
char 字符 8位
wchar_t 宽字符 16位
char16_t Unicode字符 16位
char32_t Unicode字符 32位
short 短整型 16位
int 整型 16位
long 长整型 32位
long long 长长整型 64位
float 单精度浮点 6位有效数字
double 双精度浮点 10位有效数字
long double 扩展精度浮点 10位有效数字
cpp 复制代码
// type_sizes.cpp -- 查看各类型大小
#include <iostream>
#include <climits>
#include <cfloat>

int main()
{
    using namespace std;

    cout << "===== 整型大小 =====" << endl;
    cout << "bool:      " << sizeof(bool)      << " 字节" << endl;
    cout << "char:      " << sizeof(char)      << " 字节" << endl;
    cout << "short:     " << sizeof(short)     << " 字节" << endl;
    cout << "int:       " << sizeof(int)       << " 字节" << endl;
    cout << "long:      " << sizeof(long)      << " 字节" << endl;
    cout << "long long: " << sizeof(long long) << " 字节" << endl;

    cout << "\n===== 浮点型大小 =====" << endl;
    cout << "float:       " << sizeof(float)       << " 字节" << endl;
    cout << "double:      " << sizeof(double)      << " 字节" << endl;
    cout << "long double: " << sizeof(long double) << " 字节" << endl;

    cout << "\n===== 整型范围 =====" << endl;
    cout << "int 最小值:" << INT_MIN << endl;
    cout << "int 最大值:" << INT_MAX << endl;
    cout << "unsigned int 最大值:" << UINT_MAX << endl;
    cout << "long long 最大值:" << LLONG_MAX << endl;

    cout << "\n===== 浮点型精度 =====" << endl;
    cout << "float 有效位数:" << FLT_DIG  << endl;
    cout << "double 有效位数:" << DBL_DIG << endl;

    return 0;
}

2.1.2 类型选择建议

cpp 复制代码
// type_selection.cpp -- 类型选择示例
#include <iostream>

int main()
{
    // ✅ 建议:
    // 1. 明确非负时,使用 unsigned
    // 2. 整数运算用 int,超出范围用 long long
    // 3. 浮点运算用 double(float精度不够,long double开销大)
    // 4. 算术表达式中不要混用 signed 和 unsigned

    unsigned int u = 10;
    int          i = -1;

    // ⚠️ 危险:signed 和 unsigned 混用
    // i < u 中,i 被转换为 unsigned,-1 变成很大的正数!
    if (i < (int)u)   // 正确:显式转换
        std::cout << "i < u" << std::endl;

    // unsigned 下溢
    unsigned int x = 0;
    x--;   // 变成 4294967295(UINT_MAX)
    std::cout << "0u - 1 = " << x << std::endl;

    return 0;
}

2.1.3 类型转换

cpp 复制代码
// type_conversion.cpp -- 类型转换
#include <iostream>

int main()
{
    using namespace std;

    // 隐式类型转换
    bool   b  = 42;       // b = true(非零即真)
    int    i  = b;        // i = 1
    double d  = i;        // d = 1.0
    unsigned char uc = -1; // uc = 255(取模)

    cout << "bool(42)   = " << b  << endl;   // 1
    cout << "int(true)  = " << i  << endl;   // 1
    cout << "double(1)  = " << d  << endl;   // 1
    cout << "uchar(-1)  = " << (int)uc << endl;  // 255

    // 浮点转整数:截断小数部分
    int truncated = 3.99;
    cout << "int(3.99)  = " << truncated << endl;  // 3

    // 整数转浮点:可能损失精度
    int    big = 123456789;
    float  f   = big;
    cout << "float(123456789) = " << f << endl;  // 可能不精确

    // 赋值给 bool
    bool b1 = 0;     // false
    bool b2 = -1;    // true(任何非零值)
    bool b3 = 3.14;  // true
    cout << boolalpha;
    cout << "bool(0)    = " << b1 << endl;
    cout << "bool(-1)   = " << b2 << endl;
    cout << "bool(3.14) = " << b3 << endl;

    return 0;
}

2.1.4 字面值常量

cpp 复制代码
// literals.cpp -- 字面值常量
#include <iostream>

int main()
{
    using namespace std;

    // ===== 整型字面值 =====
    int decimal = 20;      // 十进制
    int octal   = 024;     // 八进制(0开头)
    int hex     = 0x14;    // 十六进制(0x开头)

    cout << "十进制 20 = " << decimal << endl;
    cout << "八进制 024 = " << octal   << endl;   // 20
    cout << "十六进制 0x14 = " << hex  << endl;   // 20

    // 整型字面值后缀
    auto a = 42;     // int
    auto b = 42u;    // unsigned int
    auto c = 42l;    // long
    auto d = 42ul;   // unsigned long
    auto e = 42ll;   // long long
    auto f = 42ull;  // unsigned long long

    // ===== 浮点字面值 =====
    double d1 = 3.14;      // double(默认)
    float  f1 = 3.14f;     // float(f/F后缀)
    double d2 = 3.14e2;    // 科学计数法:314.0
    double d3 = 3.14E-2;   // 0.0314
    long double ld = 3.14L; // long double(L后缀)

    cout << "3.14e2  = " << d2 << endl;
    cout << "3.14E-2 = " << d3 << endl;

    // ===== 字符和字符串字面值 =====
    char   c1 = 'a';          // 字符字面值(单引号)
    char   c2 = '\n';         // 转义字符
    char   c3 = '\x41';       // 十六进制转义:'A'
    char   c4 = '\101';       // 八进制转义:'A'

    const char* s1 = "Hello"; // 字符串字面值(双引号)
    // 字符串字面值末尾自动添加 '\0'

    cout << "字符:" << c1 << " " << c3 << " " << c4 << endl;
    cout << "字符串:" << s1 << endl;

    // 字符串字面值拼接(相邻字符串自动合并)
    const char* longStr = "Hello, "
                          "World"
                          "!";
    cout << longStr << endl;

    // ===== 布尔字面值 =====
    bool t = true;
    bool fa = false;
    cout << boolalpha << t << " " << fa << endl;

    // ===== 指针字面值 =====
    int* p = nullptr;   // 空指针字面值(C++11)

    return 0;
}

常用转义字符:

转义字符 含义 转义字符 含义
\n 换行 \\ 反斜线
\t 横向制表符 \' 单引号
\r 回车 \" 双引号
\0 空字符 \a 响铃
\b 退格 \v 纵向制表符

2.2 变量

2.2.1 变量定义

cpp 复制代码
// variable_def.cpp -- 变量定义
#include <iostream>
#include <string>

int main()
{
    using namespace std;

    // 基本定义
    int    sum = 0;
    int    value;          // 未初始化(局部变量,值未定义!)
    double salary = 9999.99;
    string name   = "Alice";

    // 列表初始化(C++11,推荐)
    int    units_sold{0};   // 花括号初始化
    double price{1.99};
    string greeting{"Hello"};

    // 列表初始化的优势:防止窄化转换
    // int x{3.14};   // ❌ 编译错误!double→int是窄化转换
    int x = 3.14;    // ✅ 但会截断(x=3),有警告

    // 默认初始化
    // 全局变量:自动初始化为0
    // 局部变量:未初始化,值未定义(危险!)

    // 同时定义多个变量
    int i = 0, j = 0, k;   // i和j初始化,k未初始化
    double price1 = 1.0, price2 = 2.0;

    cout << "sum = " << sum << endl;
    cout << "salary = " << salary << endl;
    cout << "name = " << name << endl;

    return 0;
}

⚠️ 初始化 vs 赋值

  • 初始化:创建变量时赋予初始值
  • 赋值:擦除当前值,用新值替代
  • 两者在语法上相似,但概念不同

2.2.2 变量声明与定义

cpp 复制代码
// declaration_definition.cpp -- 声明与定义
// 声明:使名字为程序所知
// 定义:创建与名字关联的实体

// file1.cpp(定义)
int global_var = 42;   // 定义并初始化

// file2.cpp(声明,使用extern)
extern int global_var;   // 声明,不是定义
// extern int x = 10;   // 有初始化器的extern是定义

// 在同一文件中演示
extern int declared_var;   // 声明(定义在其他地方)

// 函数内的声明与定义
int main()
{
    using namespace std;

    int defined_var = 10;   // 定义(同时也是声明)

    // extern 在函数内部
    extern int global_var;   // 声明外部变量
    std::cout << "global_var = " << global_var << std::endl;

    return 0;
}

// 全局定义
int global_var = 42;
int declared_var = 100;

💡 关键区别

  • 变量只能定义一次 ,但可以声明多次
  • extern 声明不分配内存,定义才分配内存
  • 在多文件程序中,变量在一个文件中定义,在其他文件中用 extern 声明

2.2.3 标识符命名规则

cpp 复制代码
// naming_rules.cpp -- 命名规则示例
#include <iostream>

int main()
{
    // ✅ 合法的标识符
    int    myVariable = 1;
    int    _private   = 2;
    int    var123     = 3;
    int    camelCase  = 4;
    int    snake_case = 5;

    // ❌ 非法的标识符(注释掉)
    // int 123abc = 1;    // 不能以数字开头
    // int my-var = 2;    // 不能含连字符
    // int int    = 3;    // 不能是关键字

    // ⚠️ 保留名称(不要使用)
    // 以下划线开头的名称保留给实现使用
    // int _Reserved = 1;   // 危险!
    // int __reserved = 2;  // 危险!

    // C++ 命名惯例
    int    localVariable = 0;    // 驼峰命名(局部变量)
    int    local_variable = 0;   // 下划线命名(也常用)
    const int MAX_SIZE = 100;    // 全大写(常量)

    // 类名通常首字母大写
    // class MyClass { ... };

    return 0;
}

2.2.4 名字的作用域

cpp 复制代码
// scope_demo.cpp -- 作用域示例
#include <iostream>

int reused = 42;   // 全局作用域

int main()
{
    using namespace std;

    // 局部作用域
    int unique = 0;
    cout << "全局 reused = " << reused << endl;   // 42
    cout << "局部 unique = " << unique << endl;   // 0

    {
        // 嵌套作用域
        int reused = 0;   // 遮蔽全局变量
        cout << "\n内层 reused = " << reused << endl;   // 0(局部)
        cout << "全局 reused = " << ::reused << endl;   // 42(::访问全局)
        cout << "外层 unique = " << unique << endl;     // 0(可见)
    }
    // 内层 reused 已销毁

    cout << "\n外层 reused = " << reused << endl;   // 42(全局)

    return 0;
}

2.3 复合类型

2.3.1 引用

引用是对象的别名,必须初始化,且不能重新绑定。

cpp 复制代码
// reference_demo.cpp -- 引用详解
#include <iostream>

int main()
{
    using namespace std;

    int ival = 1024;
    int& refVal = ival;   // refVal 是 ival 的引用(别名)

    // 引用不是对象,没有自己的地址
    // 对引用的所有操作都作用于被引用的对象
    refVal = 2;           // 等价于 ival = 2
    int ii = refVal;      // 等价于 int ii = ival

    cout << "ival   = " << ival   << endl;   // 2
    cout << "refVal = " << refVal << endl;   // 2
    cout << "ii     = " << ii     << endl;   // 2

    // 引用的地址就是被引用对象的地址
    cout << "&ival   = " << &ival   << endl;
    cout << "&refVal = " << &refVal << endl;   // 相同!

    // 引用必须初始化
    // int& ref2;   // ❌ 错误:引用必须初始化

    // 引用只能绑定到对象,不能绑定到字面值或表达式
    // int& ref3 = 10;   // ❌ 错误(非const引用不能绑定右值)
    const int& ref4 = 10;   // ✅ const引用可以绑定字面值

    // 引用的类型必须与被引用对象的类型匹配
    double dval = 3.14;
    // int& ref5 = dval;   // ❌ 错误:类型不匹配
    const int& ref6 = dval;   // ✅ const引用可以(会创建临时对象)
    cout << "ref6 = " << ref6 << endl;   // 3(截断)

    // 多个引用可以绑定到同一对象
    int& r1 = ival;
    int& r2 = ival;
    int& r3 = r1;   // r3 也是 ival 的引用

    return 0;
}

2.3.2 指针

指针是指向另一种类型的复合类型,存储对象的内存地址。

cpp 复制代码
// pointer_demo.cpp -- 指针详解
#include <iostream>

int main()
{
    using namespace std;

    int ival = 42;

    // 声明指针
    int* p1;          // 未初始化的指针(危险!)
    int* p2 = &ival;  // p2 指向 ival
    int* p3 = p2;     // p3 也指向 ival

    // 取地址运算符 &
    cout << "ival 的地址:" << &ival << endl;
    cout << "p2 存储的地址:" << p2  << endl;   // 相同

    // 解引用运算符 *
    cout << "*p2 = " << *p2 << endl;   // 42
    *p2 = 100;   // 通过指针修改 ival
    cout << "ival = " << ival << endl;  // 100

    // 空指针
    int* null1 = nullptr;   // C++11 推荐
    int* null2 = 0;         // 等价
    // int* null3 = NULL;   // C风格,不推荐

    if (null1 == nullptr)
        cout << "null1 是空指针" << endl;

    // void* 指针:可以存放任意类型的地址
    double dval = 3.14;
    void* pv = &ival;   // 存放 int 的地址
    pv = &dval;         // 存放 double 的地址
    // 不能直接解引用 void* 指针

    // 指针的指针
    int** pp = &p2;   // pp 指向 p2(p2 指向 ival)
    cout << "**pp = " << **pp << endl;   // 100(ival的值)

    // 指针与引用的区别
    // 1. 引用不是对象,指针是对象
    // 2. 引用必须初始化,指针可以不初始化(但危险)
    // 3. 引用不能重新绑定,指针可以改变指向

    return 0;
}

2.3.3 指针与 const

cpp 复制代码
// pointer_const.cpp -- 指针与const
#include <iostream>

int main()
{
    using namespace std;

    int       i = 0;
    const int ci = 42;

    // 1. 指向常量的指针(底层const)
    // 不能通过指针修改所指对象的值
    const int* p1 = &ci;   // 指向const int
    const int* p2 = &i;    // 也可以指向普通int(但不能通过p2修改i)
    // *p1 = 10;   // ❌ 错误:不能修改const对象
    p1 = &i;       // ✅ 可以改变指向

    // 2. 常量指针(顶层const)
    // 指针本身是常量,不能改变指向
    int* const p3 = &i;    // const指针,指向int
    *p3 = 20;   // ✅ 可以修改所指对象
    // p3 = &ci; // ❌ 错误:不能改变指向

    // 3. 指向常量的常量指针(顶层+底层const)
    const int* const p4 = &ci;
    // *p4 = 10;  // ❌ 不能修改值
    // p4 = &i;   // ❌ 不能改变指向

    cout << "i  = " << i  << endl;
    cout << "ci = " << ci << endl;
    cout << "*p3 = " << *p3 << endl;   // 20

    // 记忆方法:从右向左读
    // const int* p  → p是指针,指向const int(底层const)
    // int* const p  → p是const指针,指向int(顶层const)
    // const int* const p → p是const指针,指向const int

    return 0;
}

2.4 const 限定符

2.4.1 const 基础

cpp 复制代码
// const_basic.cpp -- const基础
#include <iostream>

int main()
{
    using namespace std;

    // const 对象必须初始化
    const int bufSize = 512;
    // bufSize = 512;   // ❌ 错误:不能修改const对象

    // const 对象默认仅在文件内有效
    // 如果要在多个文件间共享:
    // 在头文件中:extern const int bufSize = 512;
    // 在其他文件中:extern const int bufSize;

    // const 与初始化
    const int i = 42;
    const int j = i;    // ✅ 用const初始化const
    const int k = i * 2; // ✅ 用const表达式初始化

    int x = 10;
    const int cx = x;   // ✅ 用变量初始化const(运行时确定)

    // const 引用
    const int& r1 = i;   // 对const的引用
    // r1 = 20;   // ❌ 不能通过const引用修改值

    // const 引用可以绑定非const对象
    int val = 100;
    const int& r2 = val;   // ✅
    // r2 = 200;   // ❌ 不能通过const引用修改
    val = 200;     // ✅ 可以直接修改val

    cout << "r2 = " << r2 << endl;   // 200(val改变了)

    return 0;
}

2.4.2 constexpr 与常量表达式

cpp 复制代码
// constexpr_demo.cpp -- constexpr
#include <iostream>

// constexpr 函数:编译时计算
constexpr int square(int x) { return x * x; }
constexpr int cube(int x)   { return x * x * x; }

int main()
{
    using namespace std;

    // 常量表达式:值不会改变,编译时就能得到结果
    const int max_files = 20;          // 常量表达式
    const int limit = max_files + 1;   // 常量表达式

    int staff_size = 27;               // 不是常量表达式(变量)
    // const int sz = get_size();      // 不是常量表达式(运行时才知道)

    // constexpr 变量:由编译器验证是否是常量表达式
    constexpr int mf  = 20;           // 20是常量表达式
    constexpr int lim = mf + 1;       // mf+1是常量表达式
    // constexpr int sz = staff_size; // ❌ staff_size不是常量表达式

    // constexpr 函数
    constexpr int s = square(5);   // 编译时计算:25
    constexpr int c = cube(3);     // 编译时计算:27

    cout << "square(5) = " << s << endl;
    cout << "cube(3)   = " << c << endl;

    // 用于数组大小(必须是常量表达式)
    constexpr int SIZE = 10;
    int arr[SIZE];   // ✅

    // constexpr 指针:指针本身是常量(顶层const)
    constexpr int* p = nullptr;   // p是常量指针,指向int

    return 0;
}

2.5 处理类型

2.5.1 类型别名

cpp 复制代码
// type_alias.cpp -- 类型别名
#include <iostream>
#include <string>

// 方式1:typedef(传统方式)
typedef double wages;          // wages 是 double 的别名
typedef wages base, *p;        // base 是 double,p 是 double*

// 方式2:using(C++11,推荐)
using SI = int;                // SI 是 int 的别名
using PtrToChar = char*;       // PtrToChar 是 char* 的别名

// 复杂类型别名
typedef int (*FuncPtr)(int, int);   // 函数指针类型
using FuncPtr2 = int(*)(int, int);  // 等价的using写法

int add(int a, int b) { return a + b; }

int main()
{
    using namespace std;

    wages hourly = 9.99;   // double
    base  annual = 50000;  // double
    p     ptr    = &hourly; // double*

    SI    count = 42;      // int
    cout << "hourly = " << hourly << endl;
    cout << "annual = " << annual << endl;
    cout << "*ptr   = " << *ptr   << endl;
    cout << "count  = " << count  << endl;

    // 函数指针
    FuncPtr  fp1 = add;
    FuncPtr2 fp2 = add;
    cout << "fp1(3,4) = " << fp1(3, 4) << endl;
    cout << "fp2(3,4) = " << fp2(3, 4) << endl;

    // ⚠️ 注意:typedef 与指针的陷阱
    typedef char* pstring;
    const pstring cstr = nullptr;   // cstr 是 char* const(顶层const)
    // 不是 const char*!

    return 0;
}

2.5.2 auto 类型说明符

cpp 复制代码
// auto_demo.cpp -- auto类型推断
#include <iostream>
#include <vector>
#include <map>
#include <string>

int main()
{
    using namespace std;

    // auto 让编译器推断类型
    auto i    = 0;          // int
    auto d    = 3.14;       // double
    auto s    = string("Hi"); // string

    // auto 在复杂类型中的优势
    vector<int> v = {1, 2, 3, 4, 5};
    auto it = v.begin();    // vector<int>::iterator(无需写长类型名)

    map<string, int> m = {{"a", 1}, {"b", 2}};
    for (auto& pair : m)    // auto& 推断为 pair<const string, int>&
        cout << pair.first << "=" << pair.second << " ";
    cout << endl;

    // auto 会忽略顶层 const
    const int ci = 42;
    auto  a1 = ci;    // a1 是 int(忽略顶层const)
    auto& a2 = ci;    // a2 是 const int&(保留底层const)

    // 要保留顶层const,需要显式指定
    const auto a3 = ci;   // a3 是 const int

    // auto 与引用
    int x = 10;
    auto& r = x;    // r 是 int&
    r = 20;
    cout << "x = " << x << endl;   // 20

    // auto 推断多个变量(类型必须一致)
    auto p = &x, q = &x;   // 都是 int*
    // auto p2 = &x, d2 = 3.14;  // ❌ 错误:类型不一致

    return 0;
}

2.5.3 decltype 类型指示符

cpp 复制代码
// decltype_demo.cpp -- decltype
#include <iostream>

int f() { return 42; }

int main()
{
    using namespace std;

    // decltype:推断表达式的类型,但不计算表达式
    decltype(f()) sum = 0;   // sum 的类型是 f() 的返回类型:int

    int i = 42, *p = &i, &r = i;

    // decltype 与 auto 的区别:
    // decltype 保留顶层 const 和引用
    const int ci = 0;
    decltype(ci) x = 0;   // x 是 const int
    // x = 1;   // ❌ 错误:x是const

    decltype(r) y = i;    // y 是 int&(r是引用,decltype保留引用)
    y = 10;
    cout << "i = " << i << endl;   // 10(通过y修改了i)

    // decltype 与表达式
    decltype(i)   d1 = 0;   // int(i是变量)
    decltype((i)) d2 = i;   // int&((i)是表达式,结果是左值)
    // 注意:decltype((变量)) 总是引用类型!

    decltype(i + 0) d3 = 0;  // int(i+0是右值表达式)
    decltype(*p)    d4 = i;  // int&(*p是左值)

    cout << "d1=" << d1 << " d3=" << d3 << endl;

    // 实际应用:模板中推断返回类型
    auto add = [](int a, int b) -> decltype(a + b) {
        return a + b;
    };
    cout << "add(3,4) = " << add(3, 4) << endl;

    return 0;
}

2.6 自定义数据结构

2.6.1 struct 定义与使用

cpp 复制代码
// struct_demo.cpp -- 自定义数据结构
#include <iostream>
#include <string>

// 定义结构体
struct Sales_data
{
    // 数据成员
    std::string bookNo;      // 书号
    unsigned    units_sold = 0;   // 销售量(C++11:类内初始值)
    double      revenue    = 0.0; // 收入

    // 成员函数
    double avg_price() const
    {
        if (units_sold)
            return revenue / units_sold;
        return 0;
    }
};

// 结构体可以在定义时声明变量
struct Point
{
    double x = 0.0;
    double y = 0.0;
} origin, *pPoint;   // 同时声明变量

int main()
{
    using namespace std;

    // 初始化结构体
    Sales_data data1;                          // 使用类内初始值
    Sales_data data2 = {"978-0-596-52068-7",  // 列表初始化
                        25, 15.99 * 25};

    // 访问成员
    data1.bookNo     = "978-0-201-78345-4";
    data1.units_sold = 3;
    data1.revenue    = 20.97;

    cout << "书号:" << data1.bookNo << endl;
    cout << "销量:" << data1.units_sold << endl;
    cout << "均价:" << data1.avg_price() << endl;

    // 结构体赋值
    Sales_data data3 = data1;   // 逐成员复制
    cout << "data3书号:" << data3.bookNo << endl;

    // 指针访问
    Sales_data* pData = &data1;
    cout << "通过指针:" << pData->bookNo << endl;
    cout << "通过指针:" << (*pData).bookNo << endl;

    // Point 使用
    origin.x = 0; origin.y = 0;
    Point p1 = {3.0, 4.0};
    cout << "p1: (" << p1.x << ", " << p1.y << ")" << endl;

    return 0;
}

2.6.2 头文件与预处理器

cpp 复制代码
// Sales_data.h -- 头文件示例
#ifndef SALES_DATA_H   // 头文件保护符
#define SALES_DATA_H

#include <string>

struct Sales_data
{
    std::string bookNo;
    unsigned    units_sold = 0;
    double      revenue    = 0.0;

    double avg_price() const
    {
        return units_sold ? revenue / units_sold : 0;
    }
};

#endif   // SALES_DATA_H
cpp 复制代码
// use_sales_data.cpp -- 使用头文件
#include <iostream>
#include "Sales_data.h"   // 自定义头文件用引号

int main()
{
    using namespace std;

    Sales_data total;
    if (cin >> total.bookNo >> total.units_sold >> total.revenue)
    {
        Sales_data trans;
        while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue)
        {
            if (total.bookNo == trans.bookNo)
            {
                total.units_sold += trans.units_sold;
                total.revenue    += trans.revenue;
            }
            else
            {
                cout << total.bookNo << " "
                     << total.units_sold << " "
                     << total.avg_price() << endl;
                total = trans;
            }
        }
        cout << total.bookNo << " "
             << total.units_sold << " "
             << total.avg_price() << endl;
    }
    else
    {
        cerr << "没有数据!" << endl;
        return -1;
    }
    return 0;
}

2.7 综合示例:类型系统综合运用

cpp 复制代码
// type_system.cpp -- 综合示例
#include <iostream>
#include <string>
#include <vector>
#include <cmath>

// 使用 using 定义类型别名
using Score    = double;
using Name     = std::string;
using ScoreVec = std::vector<Score>;

// 使用 constexpr 定义常量
constexpr int    MAX_STUDENTS = 100;
constexpr Score  PASS_SCORE   = 60.0;
constexpr Score  HONOR_SCORE  = 90.0;

// 结构体
struct Student
{
    Name     name;
    int      id;
    ScoreVec scores;

    // 计算平均分
    Score average() const
    {
        if (scores.empty()) return 0.0;
        Score sum = 0;
        for (const Score& s : scores)
            sum += s;
        return sum / scores.size();
    }

    // 是否通过
    bool passed() const { return average() >= PASS_SCORE; }

    // 是否优秀
    bool honored() const { return average() >= HONOR_SCORE; }
};

// 使用 auto 和 decltype
auto calcGrade(Score avg) -> char
{
    if (avg >= 90) return 'A';
    if (avg >= 80) return 'B';
    if (avg >= 70) return 'C';
    if (avg >= 60) return 'D';
    return 'F';
}

int main()
{
    using namespace std;

    // 初始化列表创建学生
    Student students[] = {
        {"张三", 1001, {85.0, 92.0, 78.0, 96.0}},
        {"李四", 1002, {70.0, 65.0, 72.0, 68.0}},
        {"王五", 1003, {95.0, 98.0, 92.0, 97.0}},
        {"赵六", 1004, {55.0, 48.0, 60.0, 52.0}}
    };

    constexpr int N = sizeof(students) / sizeof(students[0]);

    cout << "===== 成绩报告 =====" << endl;
    cout << left;

    // 使用 auto 遍历
    for (const auto& s : students)
    {
        Score avg = s.average();
        char  grade = calcGrade(avg);

        cout << "学号:" << s.id
             << " 姓名:" << s.name
             << " 平均分:" << avg
             << " 等级:" << grade;

        if (s.honored())      cout << " ⭐优秀";
        else if (s.passed())  cout << " ✓通过";
        else                  cout << " ✗未通过";
        cout << endl;
    }

    // 统计
    int passCount = 0, honorCount = 0;
    Score totalAvg = 0;

    for (const auto& s : students)
    {
        Score avg = s.average();
        totalAvg += avg;
        if (s.passed())  passCount++;
        if (s.honored()) honorCount++;
    }

    cout << "\n===== 统计信息 =====" << endl;
    cout << "总人数:" << N << endl;
    cout << "通过人数:" << passCount << endl;
    cout << "优秀人数:" << honorCount << endl;
    cout << "班级平均分:" << totalAvg / N << endl;

    return 0;
}

输出:

复制代码
===== 成绩报告 =====
学号:1001 姓名:张三 平均分:87.75 等级:B ✓通过
学号:1002 姓名:李四 平均分:68.75 等级:D ✓通过
学号:1003 姓名:王五 平均分:95.5  等级:A ⭐优秀
学号:1004 姓名:赵六 平均分:53.75 等级:F ✗未通过

===== 统计信息 =====
总人数:4
通过人数:3
优秀人数:1
班级平均分:76.4375

📝 第2章知识点总结

知识点 核心要点
算术类型 整型(bool/char/short/int/long/long long)、浮点型(float/double/long double)
类型转换 隐式转换可能丢失精度,signed/unsigned混用危险,bool非零即真
字面值 整型(十进制/八进制0/十六进制0x)、浮点(默认double)、字符(单引号)、字符串(双引号+\0)
变量初始化 列表初始化{}防止窄化转换,局部变量未初始化值未定义
声明 vs 定义 声明使名字可见,定义分配内存;extern声明不定义
引用 对象的别名,必须初始化,不能重新绑定,不是对象
指针 存储地址,可以为空(nullptr),可以改变指向
顶层/底层const 顶层const:对象本身是常量;底层const:指向的对象是常量
constexpr 编译时常量,由编译器验证,可用于数组大小等
typedef/using 类型别名,using更清晰(C++11推荐)
auto 类型推断,忽略顶层const,简化复杂类型声明
decltype 推断类型但不计算,保留顶层const和引用,decltype((var))是引用
struct 自定义数据结构,支持类内初始值(C++11),成员默认public
头文件保护 #ifndef/#define/#endif 防止重复包含
相关推荐
在繁华处1 小时前
Java从零到熟练(三):流程控制
java·开发语言·python
云泽8082 小时前
C++ 可调用对象通关指南:深度解析 Lambda 表达式、function 包装器与 bind 绑定器
开发语言·c++·算法
Tri_Function3 小时前
简单图论大学习
c++
lqqjuly3 小时前
C++ 完整知识体系—从基础语法到现代 C++23 的系统性总结
c++·c++23
王老师青少年编程4 小时前
信奥赛C++提高组csp-s之FHQ Treap
c++·csp·平衡树·信奥赛·csp-s·提高组·fhq treap
星恒随风4 小时前
Python 基础语法详解(一):从表达式、变量到数据类型
开发语言·笔记·python·学习
888CC++4 小时前
java 并发编程
java·开发语言·python
罗超驿4 小时前
18.Web API 实战:元素与表单属性的获取和修改
开发语言·前端·javascript
被子你放开我5 小时前
CRMEB PHP多商户升级4.0太麻烦了
开发语言·php