C++入门(2)

一、引用(&)

1. 引用的概念和定义

• 本质:给已存在的变量取别名,编译器不会为引用变量开辟独立内存空间,与引用对象共用同一块内存。

• 语法格式:类型& 引用别名 = 引用对象;

• 示例:

cpp 复制代码
int a = 0;
int& b = a;  // b是a的别名
int& c = a;  // 一个变量可多个引用
int& d = b;  // 给别名取别名,最终仍指向a
++d;         // 操作d等价于操作a
// 输出地址相同,验证共用内存
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;

2. 引用的三大特性

  1. 必须初始化:声明引用时必须绑定对象,不能单独声明(如int& ra; 编译报错)。

  2. 一个变量可多个引用:同一变量可被多个引用别名指向。

  3. 引用不可更改指向:一旦绑定某个对象,无法再绑定其他对象(后续赋值是修改对象值,而非改指向)。

• 示例:

cpp 复制代码
int a = 10;
int& b = a;
int c = 20;
b = c;  // 不是改指向,是将c的值赋给a(b是a的别名)

3. 引用的使用场景

(1)引用传参(替代指针传参,简化语法)

• 作用:函数内修改实参,避免指针解引用的繁琐,可读性更高。

• 示例(交换函数):

cpp 复制代码
void Swap(int& rx, int& ry) {
    int tmp = rx;
    rx = ry;
    ry = tmp;
}
int main() {
    int x = 0, y = 1;
    Swap(x, y);  // 直接传变量,无需取地址
    cout << x << " " << y << endl;
    return 0;
}

(2)引用做返回值(减少拷贝,提高效率)

• 作用:返回对象的别名,避免值返回的拷贝开销,可直接修改返回对象。

• 示例(栈顶元素返回):

cpp 复制代码
typedef int STDataType;
typedef struct Stack {
    STDataType* a;
    int top;
    int capacity;
} ST;
// 引用返回栈顶元素,可直接修改
STDataType& STTop(ST& rs) {
    return rs.a[rs.top];
}
int main() {
    ST st1;
    STInit(st1);
    STPush(st1, 1);
    STTop(st1) += 10;  // 直接修改栈顶值
    cout << STTop(st1) << endl;
    return 0;
}

(3)指针引用(替代二级指针,简化链表操作)

• 作用:给指针变量取别名,无需二级指针即可修改指针本身。

• 示例(链表尾插):

cpp 复制代码
typedef struct ListNode {
    int val;
    struct ListNode* next;
} LTNode, *PNode;
// 指针引用phead,直接修改头指针
void ListPushBack(PNode& phead, int x) {
    PNode newnode = (PNode)malloc(sizeof(LTNode));
    newnode->val = x;
    newnode->next = NULL;
    if (phead == NULL) {
        phead = newnode;  // 直接修改头指针
    } else {
        // 遍历尾插逻辑...
    }
}
int main() {
    PNode plist = NULL;
    ListPushBack(plist, 1);  // 直接传指针,无需传地址
    return 0;
}

4. const 引用(权限控制)

(1)核心规则

• 可引用const对象,必须用const引用(权限不能放大);

• const引用可引用普通对象(权限缩小,合法)。

(2)临时对象与const引用

• 表达式求值、类型转换会产生临时对象(编译器自动创建的未命名对象,具有常量性);

• 临时对象只能用const引用绑定,普通引用会编译报错。

• 示例:

cpp 复制代码
const int a = 10;
// int& ra = a;  报错:权限放大(const→非const)
const int& ra = a;  // 合法

int b = 20;
const int& rb = b;  // 合法:权限缩小(非const→const)
// rb++;  报错:const引用不可修改

// 临时对象绑定(必须用const引用)
const int& rb = a * 3;  // a*3产生临时对象,const引用绑定
double d = 12.34;
const int& rd = d;      // 类型转换产生临时对象,const引用绑定

5. 引用与指针的核心区别

|----------|-------------------|-----------------------|
| 对比维度 | 引用 | 指针 |
| 内存占用 | 不开辟独立内存,共用对象内存 | 开辟独立内存(存地址) |
| 初始化 | 必须初始化 | 可声明后赋值(语法不强制) |
| 指向修改 | 绑定后不可改指向 | 可随时改指向 |
| 访问方式 | 直接访问对象,无需解引用 | 需解引用(*)访问对象 |
| 空值 | 无空引用,更安全 | 可指向NULL/nullptr,易出空指针 |
| sizeof结果 | 引用类型的大小(如int&→4) | 地址空间大小(32位4/64位8) |

二、inline 关键字(内联函数)

1. 核心概念

• 定义:inline修饰的函数为内联函数,编译时编译器会在调用处直接展开函数体,避免函数调用的栈帧开销,提高执行效率。

• 本质:是编译器建议,而非强制命令------编译器会根据函数复杂度(如代码长度、是否递归)决定是否展开,C++标准无强制展开规则。

2. 适用场景

• 适合:频繁调用、代码简短的函数(如简单的加减、取值函数);

• 不适合:递归函数、代码冗长的函数(编译器会自动忽略inline)。

3. 与C语言宏函数的对比

• 宏函数:预处理阶段文本替换,无类型检查,易出错(如括号缺失导致运算优先级问题);

• inline函数:编译阶段处理,有类型安全检查,语法与普通函数一致,替代宏函数更安全。

• 宏函数易错示例:

cpp 复制代码
// 错误宏(无括号,优先级问题)
#define ADD(a,b) a+b
cout << ADD(1,2)*5 << endl;  // 展开为1+2*5=11,不符合预期
// 正确宏(加括号保证优先级)
#define ADD(a,b) ((a)+(b))
cout << ADD(1,2)*5 << endl;  // 展开为(1+2)*5=15,符合预期

4. inline函数的使用规范

  1. 声明与定义不分离:若将inline函数声明放头文件、定义放源文件,链接时会报"无法解析外部符号"(内联展开后无函数地址,链接器找不到定义)。

◦ 错误示例:

cpp 复制代码
// F.h(声明)
inline void f(int i);
// F.cpp(定义)
void f(int i) { cout << i << endl; }
// main.cpp(调用)
#include "F.h"
f(10);  // 链接报错:无法解析外部符号
  1. Debug模式默认不展开:VS等编译器的Debug版本为方便调试,默认关闭inline展开,Release版本自动开启。

  2. 代码示例

cpp 复制代码
#include <iostream>
using namespace std;
// 内联函数:简单加法,适合展开
inline int Add(int x, int y) {
    int ret = x + y;
    ret += 1;
    return ret;
}
int main() {
    int ret = Add(1, 2);  // 编译时直接展开为int ret=1+2+1;
    cout << ret << endl;
    return 0;
}

三、nullptr(C++11空指针关键字)

1. 传统NULL的缺陷

• 本质:NULL是宏,C++中定义为0,C中定义为(void*)0;

• 问题:函数重载时,NULL会被优先匹配int参数版本,而非指针版本,违背空指针使用初衷。

• 示例:

cpp 复制代码
void f(int x) { cout << "f(int x)" << endl; }
void f(int* ptr) { cout << "f(int* ptr)" << endl; }
int main() {
    f(NULL);  // 匹配f(int x),而非预期的f(int* ptr)
    f((int*)NULL);  // 强制转换才能匹配指针版本,繁琐
    return 0;
}

2. nullptr的优势

• 类型:C++11新增关键字,是特殊指针类型字面值,只能隐式转换为任意指针类型,不能转换为整数类型;

• 作用:完美表示空指针,避免NULL的类型歧义,重载时优先匹配指针参数。

• 示例:

cpp 复制代码
void f(int x) { cout << "f(int x)" << endl; }
void f(int* ptr) { cout << "f(int* ptr)" << endl; }
int main() {
    f(nullptr);  // 直接匹配f(int* ptr),无需强制转换
    return 0;
}

3. 核心规则

• nullptr是指针类型,NULL是整数0(C++);

• 空指针定义优先用nullptr,替代NULL,避免类型歧义。

相关推荐
蜜桃味女焊匠人2 小时前
焊接生产线优化思路:解决手工焊、机器人焊气体浪费问题
人工智能·经验分享·其他·机器人
Scott9999HH8 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
2401_841495648 小时前
【操作系统】进程同步与互斥实验报告
c++·算法·操作系统·进程·并发·同步·互斥
fqbqrr9 小时前
2607C++,soui与安卓
c++·soui
码智社9 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海9 小时前
python 列表、元组、集合和字典
开发语言·python
克里斯蒂亚诺更新9 小时前
ruoyi角色权限的对应关系
笔记
萧瑟余晖10 小时前
JDK 26 新特性详解
java·开发语言
马优晨10 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
a11177612 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习