快速从C过度C++(二):引用,内联函数,nullptr
📝前言:
本文章适合有一定C语言编程基础的读者浏览,主要介绍从C语言到C++过度,我们首先要掌握的一些基础知识,以便于我们快速进入C++的学习,为后面的学习打下基础。上一篇文章快速从C过度C++,我们讲述了namespace
,C++的输入和输出,缺省参数,函数重载。
这篇文章主要介绍:
1,引用
2,内联函数
3,nullptr
🎬个人简介:努力学习ing
📋个人专栏:C++学习笔记
🎀CSDN主页 愚润求学
🌄其他专栏:C语言入门基础,python入门基础,python刷题专栏
快速从C过度C+(二)
- 一,引用
- 1. 引用的介绍
- 2. 引用的特点
- 3. 引用的使用
- 3.1. 作为函数参数传递
- 3.2. 作为函数返回值
- 3.3. 权限放大错误
- 4. const引用
- 4.1. const引用的基本语法
- 4.2. const 引用的特性
- 4.3. const引用的常见用途
- 4.3.1. 函数参数传递
- 4.3.2. 绑定到临时对象
- 4.3.3. 避免权限放大错误
- 4.4. const引用与普通引用的区别
- 5. 引用与指针的关系
- 二,内联函数inline
- 1. 宏函数
- 2. 内敛函数
- 2.1. 主要特点
- 2.2. 使用场景
- 三,nullptr
- 1. nullptr 和 NULL 的区别
- 2. 为什么需要 nullptr?
一,引用
1. 引用的介绍
C++的引用是给变量取别名,编译器不会为引用变量开辟新的内存空间,它和它引用的变量共用一块内存空间。
- 定义引用:引用在定义时必须初始化,并且一旦初始化后,就不能再指向其他变量。
- 语法:使用
&
符号来声明引用。 - 示例:
int x = 10; int &ref = x; // ref 是 x 的引用 ref = 20; // 修改 ref 也会修改 x std::cout << x; // 输出 20
2. 引用的特点
- 必须初始化:引用在声明时必须初始化,不能先声明后赋值。
- 不可重新绑定:引用一旦绑定到一个变量,就不能再绑定到其他变量(即:不能改变指向)。
- 多个别名:一个变量可以有多个引用
- 无空引用:引用必须指向一个有效的对象,不能像指针那样可以为
nullptr
。 - 自动解引用:使用引用时不需要像指针那样使用
*
来解引用。
3. 引用的使用
3.1. 作为函数参数传递
引用常用于函数参数传递,特别是当你想在函数内部修改传入的变量时。
- 示例:
void increment(int &num) {
num++;
}
int main() {
int a = 5;
increment(a);
std::cout << a; // 输出 6
return 0;
}
在这里,num
是a
的别名,所以修改了num
就是真实的修改了a
3.2. 作为函数返回值
引用也可以作为函数的返回值,通常用于返回类成员变量或静态变量(即常量)。
- 示例:
int& getValue() {
static int value = 10;
return value;
}
int main() {
int &ref = getValue(); 引用接收常量
ref = 20;
std::cout << getValue(); // 输出 20
return 0;
}
3.3. 权限放大错误
权限:只读,可读写…
下面三种情况,会生成一个临时变量存放结果,这个临时变量具有常性(只读性):
- 表达式返回
- 函数返回
- 类型转换(提升和截断都属于)
这个临时变量可以引用绑定,当被绑定后,就不会被销毁(生命周期)
但是,我们在绑定这个临时变量时要注意权限放大错误,如下:
int main() {
int a = 1;
int b = 2;
int &c = a + b;
return 0;
}
出现权限放大错误,因为a + b
返回时,会产生一个具有只读性的临时变量,但是企图用int
类型来接受它,放大了权限,导致错误。权限可以缩小,但是不能放大。
类似的还有:
int main() {
double a = 3.14;
int &b = int(a);
return 0;
}
int ADD(int a, int b) {
return a + b;
}
int main() {
int& c = ADD(1, 2);
return 0;
}
const int a = 10;
int& ra = a;
4. const引用
在C++中,const
引用(常量引用)是一种特殊的引用类型,它允许你通过引用的方式访问一个对象,但不能通过该引用修改对象的值。修改引用对象,被引用对象也被修改
4.1. const引用的基本语法
const
引用通过在引用声明前加上 const
关键字来定义。它的语法如下:
const T& ref = obj;
T
是类型。ref
是引用的名称。obj
是被引用的对象。
示例:
int x = 10;
const int& ref = x; // ref 是 x 的 const 引用
4.2. const 引用的特性
-
只读性:通过
const
引用,只能读取对象的值,不能修改对象的值。int x = 10; const int& ref = x; std::cout << ref; // 正确:读取值 ref = 20; // 错误:不能通过 const 引用修改值
-
绑定到临时对象:
const
引用可以绑定到临时对象(右值),并且会延长临时对象的生命周期。(因为const也只具有只读性,不会有权限放大的问题)const int& ref = 42; // 正确:绑定到临时对象
-
避免拷贝:
const
引用可以避免不必要的对象拷贝,特别是在函数参数传递时。void printValue(const std::string& str) { std::cout << str; } int main() { std::string s = "Hello, World!"; printValue(s); // 避免拷贝 return 0; }
4.3. const引用的常见用途
4.3.1. 函数参数传递
const
引用常用于函数参数传递,特别是当函数不需要修改参数的值时。这种方式可以避免不必要的拷贝,同时保证参数的值不会被意外修改。
- 示例:
void printValue(const int& value) { std::cout << value; } int main() { int x = 10; printValue(x); // 传递 x 的 const 引用 return 0; }
4.3.2. 绑定到临时对象
const
引用可以绑定到临时对象(右值),并且会延长临时对象的生命周期。
- 示例:
const std::string& getString() { return "Hello, World!"; // 返回临时对象的 const 引用 } int main() { const std::string& ref = getString(); // 延长临时对象的生命周期 std::cout << ref; // 输出 "Hello, World!" return 0; }
4.3.3. 避免权限放大错误
const
引用可以防止权限放大错误,即防止将只读对象转换为可读写对象。
- 示例:
int getValue() { return 42; // 返回临时对象 } int main() { const int& ref = getValue(); // 正确:绑定到临时对象的 const 引用 // int& ref = getValue(); // 错误:权限放大错误 return 0; }
4.4. const引用与普通引用的区别
特性 | const 引用 | 普通引用 |
---|---|---|
是否可修改对象 | 不可修改 | 可修改 |
能否绑定到临时对象 | 可以 | 不可以 |
权限 | 只读 | 可读写 |
常见用途 | 函数参数传递、绑定临时对象 | 需要修改对象时使用 |
5. 引用与指针的关系
- 语法概念上,引用就是取别名,不开空间,指针是存储一个地址,要开空间
- 底层,引用是用指针实现的
- 引用定义必须初始化,指针只是建议初始化,但是非必须
- 引用绑定一个对象以后不能修改,指针可以
sizeof
的含义不同,引用结果大小为引用类型的大小,指针始终为地址空间所占字节个数- 指针容易出现空指针和野指针
二,内联函数inline
1. 宏函数
宏函数(通常也称为宏定义)是在编程中一种预处理指令,它允许你定义一个标识符来代表一段代码片段。在编译之前,预处理器会将源文件中所有使用该标识符的地方替换为对应的代码片段。
#define 宏名(参数列表) 替换文本
注意:
1,替换文本后面不接;
2,替换文本中要多用括号来体现优先级,不然容易出现替换后的优先级问题
示例:
#define ADD(x, y) ((x) - (y))
2. 内敛函数
内联函数(Inline Function) 是C++中的一种函数优化机制,通过在函数定义前加上 inline
关键字,建议编译器将函数调用处直接替换为函数体代码,以减少函数调用的开销。
2.1. 主要特点
- 减少开销:避免函数调用的压栈、跳转和返回操作,提升效率。
- 代码膨胀:函数体被直接插入调用处,可能增加生成的可执行文件大小。
- 编译器决定:
inline
只是建议,编译器有权决定是否内联,通常适用于简单、频繁调用的函数。当函数过于复杂时,编译器依旧会当做普通的函数来运行,开辟栈帧。 - 调试困难:内联函数在调试时可能难以跟踪。
2.2. 使用场景
- 短小函数:适合代码简单、调用频繁的函数。递归或复杂函数不适合内联。
- 性能关键代码:在需要优化性能时使用。
- 替代宏:内联函数比宏更安全,能进行类型检查。
示例:
#include <iostream>
using namespace std;
inline int add(int a, int b) {
return a + b;
}
int main() {
int result = add(3, 4); // 可能被替换为 int result = 3 + 4;
cout << "Result: " << result << endl;
return 0;
}
三,nullptr
1. nullptr 和 NULL 的区别
nullptr
是 C++11 引入的关键字,用于表示空指针。它解决了 C 语言中 NULL
的一些问题,提供了更安全、更清晰的空指针表示方式。
特性 | nullptr (C++11) | NULL (C/C++) |
---|---|---|
类型 | 是 std::nullptr_t 类型,可以隐式转换为任意指针类型 | 通常是 0 或 (void*)0 ,是一个宏定义 |
类型安全 | 类型安全,不会与整数类型混淆 | 可能被误认为是整数类型(如 int ) |
重载函数解析 | 能正确区分指针和整数重载 | 可能导致重载函数解析错误 |
代码清晰性 | 明确表示空指针,语义清晰 | 语义不够明确,可能被误解为整数 0 |
2. 为什么需要 nullptr?
-
类型安全问题
- 在 C++ 中,
NULL
通常被定义为0
或(void*)0
,这可能导致类型混淆。例如:
使用void foo(int); void foo(char*); foo(NULL); // 调用 foo(int),而不是 foo(char*)
nullptr
可以避免这种问题:foo(nullptr); // 明确调用 foo(char*)
- 在 C++ 中,
-
重载函数解析
NULL
可能被当作整数0
,导致调用错误的重载函数。nullptr
明确表示指针类型,能正确匹配指针重载。
-
代码清晰性
nullptr
明确表示空指针,提高了代码的可读性和语义清晰性。
-
C++ 类型系统的改进
nullptr
是std::nullptr_t
类型,可以隐式转换为任意指针类型,但不能转换为整数类型,增强了类型检查。
示例:
#include <iostream>
using namespace std;
void func(int) {
cout << "Called func(int)" << endl;
}
void func(char*) {
cout << "Called func(char*)" << endl;
}
int main() {
func(0); // 调用 func(int)
func(NULL); // 可能调用 func(int),取决于 NULL 的定义
func(nullptr); // 明确调用 func(char*)
int* ptr = nullptr; // 正确:nullptr 可以赋值给指针
// int num = nullptr; // 错误:nullptr 不能赋值给整数类型
return 0;
}
🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!