举爪爪打招呼

很高兴你点开这篇文章✨

这里会持续更新我喜欢的内容,关注我,一起慢慢变好呀

👍 点赞 ⭐ 收藏 💬 评论


前言

学完引用之后,你是否也和我一样有疑问:const 引用是什么?什么时候用?还有,C++ 里怎么定义常量?NULL 和 nullptr 到底有什么区别?

下面我们就一起来探讨这些问题吧!


一、const 引用

1.1 什么是 const 引用?

const 引用就是只读的引用。你可以通过它读取数据,但不能通过它修改数据。

int a = 10;
const int& r = a;   // r 是 a 的 const 引用

// r = 20;   // ❌ 报错:不能通过 const 引用修改
a = 20;     // ✅ 但可以通过原变量修改

1.2 const 引用的三大特性

特性 说明
可读不可写 不能通过 const 引用修改值
可以绑定常量 const int& r = 10; 合法
可以绑定临时对象 函数返回的临时值也能绑定

比如:

const int& r1 = 10;        // ✅ 绑定常量
const int& r2 = a + b;     // ✅ 绑定表达式结果(临时对象)

1.3 权限的三种情况(非常重要)

C++ 中引用传递权限只能平移或缩小,不能放大。

情况 原变量权限 引用权限 是否允许
平移 可读可写 可读可写
缩小 可读可写 只读(const)
放大 只读(const) 可读可写 禁止

示例:

//权限平移---普通变量->普通引用,权限不变,都是可读可写
int a=10;			a:可读可写
int& ra=a;			ra:可读可写

//权限缩小---普通变量->const引用,把“可写”的权限收回去,变成只可读
int a=10;			a:可读可写
const int& ra=a;	ra:只能读

//权限放大---const引用->普通引用,想把只读变成可写,违法!
const int a=10;		a:只可读
int& ra=a;			ra:想又可读又可写,绝对不可以!

1.4 const 引用的典型应用:函数参数

void print(const int& x) 
{
    // x 是只读的,不会意外修改外面的值
    // 同时避免了拷贝,效率高
    cout << x << endl;
}

🐾 为什么用 const 引用传参?

  • 避免拷贝(大对象时效率高)
  • 保证不修改原数据(安全)
  • 可以接收常量和临时对象(灵活)

🐶 🐾 ✨ 🐾 🐶


二、inline 内联函数

2.1 为什么需要 inline?

C 语言中用宏(#define)来定义小函数,但宏有很多坑:

#define ADD(a, b) ((a) + (b))   // 必须加一堆括号,很麻烦

🐾 C++ 提供了 inline 关键字,用来建议编译器把函数调用展开成代码,消除函数调用的开销。

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

int ret = add(1, 2);   // 编译后可能直接变成 int ret = 1 + 2;

2.2 inline 的特点

特点 说明
建议性 inline 只是给编译器的建议,是否展开由编译器决定
适合短小函数 函数体太长,编译器会忽略 inline
不能递归 递归函数无法内联展开
声明和定义不能分离 通常直接写在头文件里

2.3 宏 vs inline

对比项 宏(#define) inline 函数
类型检查 没有
调试支持 不可调试 可调试
作用域 全局预处理器 遵循 C++ 作用域规则
参数求值 可能多次求值 正常求值一次

宏的问题示例:

#define SQUARE(x) ((x) * (x))
int a = 2;
int b = SQUARE(++a);   // 展开后:(++a) * (++a) -> a 被加了两次!


// inline 函数不会有这个问题
inline int square(int x) 
{
    return x * x;
}
int c = square(++a);   // 正常,a 只加一次

三、nullptr 空指针

3.1 问题的由来:NULL 是什么?

在 C++ 中,NULL 通常被定义为 0 或 (void*)0,这会导致函数重载的二义性问题。

void f(int x) 
{
    cout << "f(int x)" << endl;
}

void f(int* ptr) 
{
    cout << "f(int* ptr)" << endl;
}

int main() 
{
    f(0);           // 调用 f(int)
    f(NULL);        // 调用 f(int) 还是 f(int*)?大多数情况是 f(int)
    f((int*)NULL);  // 强制转成指针,才能调用 f(int*)
}

🐾 问题:NULL 会被当成整数 0,而不是指针,导致调错函数。


3.2 nullptr 的解决方案

C++11 引入了 nullptr,它是类型安全的空指针。

f(nullptr);   // 明确调用 f(int*),不会混淆
对比项 NULL nullptr
类型 整数(通常是 0) std::nullptr_t
是否可转成整数 可以 不可以隐式转
是否可转成指针 可以(但会二义) 可以
函数重载 可能调错 总能正确匹配指针版本

3.3 nullptr 的使用

int* p1 = NULL;      // 能用,但不推荐
int* p2 = nullptr;   //  推荐

char* p3 = nullptr;  //  可以转成任何指针类型
// int a = nullptr;  // ❌ 不能转成整数

// 判断空指针
if (p2 == nullptr) 
{
    cout << "p2 is null" << endl;
}

🐾 建议一律使用 nullptr,不要再用 NULL!!


四、知识点汇总

知识点 核心要点
const 引用 只读引用,可绑常量和临时对象,权限只能缩小不能放大
const 引用传参 避免拷贝 + 只读保护 + 灵活接收常量
inline 建议编译器展开函数,消除调用开销,适合短小函数
inline vs 宏 inline 有类型检查、可调试、更安全
nullptr 类型安全的空指针,解决 NULL 的重载二义性

五、常见面试题

🐾 Q1:const int& 能绑定到 int 变量吗?能绑定到 int 常量吗?

int a = 10;
const int& r1 = a;    //  能
const int& r2 = 20;   //  能

🐾 Q2:inline 函数一定能内联成功吗?

  • 不一定。inline 只是建议,编译器对长函数、递归函数通常会忽略。

🐾 Q3:nullptr 的类型是什么?

  • std::nullptr_t ,可以隐式转换成任何指针类型,但不能转成整数。

🐶 🐾 ✨ 🐾 🐶


举爪爪求关注

谢谢你看到这里呀

如果喜欢这篇内容,点个关注,下次更新不迷路✨

👍 点赞 ⭐ 收藏 💬 评论

更多推荐