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,避免类型歧义。

相关推荐
WeiXiao_Hyy2 小时前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
ZH15455891312 小时前
Flutter for OpenHarmony Python学习助手实战:面向对象编程实战的实现
python·学习·flutter
User_芊芊君子2 小时前
CANN010:PyASC Python编程接口—简化AI算子开发的Python框架
开发语言·人工智能·python
小羊不会打字2 小时前
CANN 生态中的跨框架兼容桥梁:`onnx-adapter` 项目实现无缝模型迁移
c++·深度学习
Max_uuc2 小时前
【C++ 硬核】打破嵌入式 STL 禁忌:利用 std::pmr 在“栈”上运行 std::vector
开发语言·jvm·c++
简佐义的博客2 小时前
生信入门进阶指南:学习顶级实验室多组学整合方案,构建肾脏细胞空间分子图谱
人工智能·学习
故事不长丨2 小时前
C#线程同步:lock、Monitor、Mutex原理+用法+实战全解析
开发语言·算法·c#
近津薪荼2 小时前
dfs专题4——二叉树的深搜(验证二叉搜索树)
c++·学习·算法·深度优先
牵牛老人2 小时前
【Qt 开发后台服务避坑指南:从库存管理系统开发出现的问题来看后台开发常见问题与解决方案】
开发语言·qt·系统架构