C++ 中 符号的主要用法详解
在 C 中符号的含义随上下文变化核心分为三类引用声明类型的一部分、取地址运算单目操作符、按位与运算双目操作符。准确判断其含义的关键是观察出现在声明语句定义变量/函数参数还是表达式操作变量中。一、作为“引用类型”声明语句中的类型标识引用是 C 特有的“别名”机制用于为现有变量创建新名称本质是编译期的别名无独立内存与目标变量共享内存地址。在声明语句中紧跟类型名后用于标识“引用类型”。语法规范Type refName existingVariable; // 类型 引用名 目标变量必须初始化关键规则引用必须在声明时初始化且初始化后不能重新绑定到其他变量区别于指针。示例代码int x 10; // 定义普通变量x int ref x; // ref是x的引用别名共享内存地址 ref 20; // 修改ref等同于修改x cout x; // 输出20x的值被改变常见应用场景函数参数传递避免大对象拷贝提升性能如传递std::vector、std::string等。例如一个计算大型向量的总和的函数// 优秀的写法按常量引用传递 // 不拷贝直接操作原始数据且用 const 承诺不修改 long long sumByConstRef(const std::vectorint data) { long long sum 0; for (int num : data) { sum num; } // 无析构开销因为没创建新对象 return sum; }完整的对比测试程序如下#include iostream #include vector #include chrono // 用于计时 // 糟糕的写法按值传递 // 每次调用都会完整复制整个 vector对于大对象开销巨大 long long sumByValue(std::vectorint data) { long long sum 0; for (int num : data) { sum num; } // 函数结束data 析构释放拷贝的内存 return sum; } // 优秀的写法按常量引用传递 // 不拷贝直接操作原始数据且用 const 承诺不修改 long long sumByConstRef(const std::vectorint data) { long long sum 0; for (int num : data) { sum num; } // 无析构开销因为没创建新对象 return sum; } int main() { // 创建一个包含 1000 万个整数的大向量约 40MB 数据 const size_t SIZE 10000000; std::vectorint hugeVec(SIZE, 42); // 测试按值传递 auto start std::chrono::high_resolution_clock::now(); long long result1 sumByValue(hugeVec); auto end std::chrono::high_resolution_clock::now(); auto duration1 std::chrono::duration_caststd::chrono::milliseconds(end - start).count(); std::cout 按值传递结果: result1 耗时: duration1 毫秒 std::endl; // 测试按常量引用传递 start std::chrono::high_resolution_clock::now(); long long result2 sumByConstRef(hugeVec); end std::chrono::high_resolution_clock::now(); auto duration2 std::chrono::duration_caststd::chrono::milliseconds(end - start).count(); std::cout 按常量引用传递结果: result2 耗时: duration2 毫秒 std::endl; std::cout \n性能提升: 按值传递耗时是按常量引用传递的 (duration1 / (duration2 0 ? duration2 : 1)) 倍 std::endl; return 0; }函数返回值支持链式调用如流操作符、或返回类成员引用避免拷贝。std::ostream operator(std::ostream os, const std::string str) { os str; // 返回os引用支持cout s1 s2; return os; }多态实现基类引用绑定派生类对象实现动态绑定运行时调用派生类方法。class Base { public: virtual void print() { cout Base\n; } }; class Derived : public Base { public: void print() override { cout Derived\n; } }; int main() { Derived d; Base ref d; // 基类引用绑定派生类对象 ref.print(); // 输出Derived动态绑定到Derived::print }进阶用法右值引用右值引用是一种专门用来绑定右值临时对象、字面量等“用完即毁”的值的引用类型。它最大的价值在于实现了移动语义和完美转发从而大幅提升性能。1.移动语义 (Move Semantics) —— 变“拷贝”为“窃取”这是右值引用最强大的用途。在C98中用临时对象初始化一个新对象会触发深拷贝分配新内存、复制数据对大对象如std::vector、std::string来说非常昂贵。右值引用允许我们编写移动构造函数直接“窃取”临时对象内部的资源如内存指针而不是复制。整个过程只涉及指针交换时间复杂度从 O(n) 降为 O(1)。// 移动构造函数 (C11 风格 - 快) MyString(MyString other) noexcept { data other.data; // 直接窃取指针 other.data nullptr; // 原对象置空防止析构时重复释放 std::cout 移动构造\n; }2.完美转发 (Perfect Forwarding) —— 保持参数的原始特性在模板编程中我们希望函数能将参数原封不动地转发给另一个函数同时保持参数是左值还是右值的特性。右值引用配合std::forward实现了这一点。#include iostream void process(int x) { std::cout 处理左值: x \n; } void process(int x) { std::cout 处理右值: x \n; } template typename T void forward_func(T arg) { process(std::forwardT(arg)); // 保持arg的原始特性 } int main() { int a 5; forward_func(a); // a是左值 → 输出: 处理左值: 5 forward_func(10); // 10是右值 → 输出: 处理右值: 10 return 0; }二、作为“取地址运算符”表达式中的单目操作符在表达式中作为单目运算符用于获取变量的内存地址返回值为指向该变量的指针类型为Type*。语法规范variable // 变量前加返回其内存地址如x的类型为int*若x是int关键规则操作数必须是左值有明确内存地址的变量不能是临时对象如字面量5或表达式结果如x y。示例代码int x 42; // 定义变量x int* ptr x; // ptr存储x的内存地址x返回int* cout ptr; // 输出x的地址如0x7fff5fbff6ac cout *ptr; // 解引用ptr输出x的值42常见应用场景指针初始化将变量地址赋给指针通过指针间接操作变量。int y 100; int* p y; // p指向y *p 200; // 通过p修改y的值y变为200函数传址通过指针修改函数外部的变量如swap函数。void swap(int* a, int* b) { int temp *a; *a *b; *b temp; } int main() { int a 1, b 2; swap(a, b); // 传递a、b的地址 cout a b; // 输出2 1 }三、作为“按位与运算符”表达式中的双目操作符在表达式中作为双目运算符对两个整数的二进制位进行“与”操作对应位均为1时结果位为1否则为0。语法规范a b // 两个整数a、b的二进制位按位与如5 3 1关键规则操作数必须是整数类型int、char、unsigned long等不能是浮点数或类对象除非重载了运算符。示例代码int a 5; // 二进制: 0101 int b 3; // 二进制: 0011 int c a b; // 二进制: 0001十进制1 cout c; // 输出1常见应用场景位掩码提取或设置整数的特定位如权限检查。const int READ_PERMISSION 0x01; // 0001 const int WRITE_PERMISSION 0x02; // 0010 int permissions READ_PERMISSION | WRITE_PERMISSION; // 0011读写权限 if (permissions READ_PERMISSION) { // 检查是否有读权限 cout Read permission granted.\n; }数据压缩通过位运算将多个布尔值存储到一个整数中节省内存。int flags 0; flags | 0x01; // 设置第0位为1标志1开启 flags ~0x02; // 清除第1位标志2关闭 if (flags 0x01) { // 检查标志1是否开启 cout Flag 1 is on.\n; }快速判断含义的关键技巧上下文含义示例代码声明语句中的类型部分如Type name引用类型别名int ref x;表达式中的单目操作如variable取地址返回指针int* p x;表达式中的双目操作如a b按位与位运算int c a b;口诀“声明看类型表达看操作”——在类型声明中是引用的一部分在表达式中单目是取地址双目是按位与。