我的C++奇迹之旅:值和引用的本质效率与性能比较2

简介: 我的C++奇迹之旅:值和引用的本质效率与性能比较

我的C++奇迹之旅:值和引用的本质效率与性能比较1:https://developer.aliyun.com/article/1474577

🌠引用做返回值

int& Count()
{
   static int n = 0;
   n++;
   // ...
   return n;
}
• 1
• 2
• 3
• 4
• 5
• 6
• 7
int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    Add(3, 4);
    cout << "Add(1, 2) is :"<< ret <<endl;
    return 0;
}

在Vs编译运行图:结果是7,真的是正确吗?

问题分析:

如果函数返回时,返回的对象已经超出了函数作用域(即已经被销毁),那么不能返回该对象的引用,必须返回值。

在第一个示例中:

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}

这里函数返回了局部变量c的引用,但c在函数返回后就已经被销毁了,所以这是一个未定义行为,输出结果是不确定的。

而在第二个示例中:

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    Add(3, 4);
    cout << "Add(1, 2) is :"<< ret <<endl;
    return 0;
}

里同样是返回了局部变量c的引用,但是在main函数中又调用了一次Add函数,这时第一次调用返回的引用ret已经指向了一个不存在的对象,所以输出结果也是未定义的。


函数返回引用时必须确保返回的对象在调用者作用域内仍然存在,否则就会产生未定义行为。这是C++中函数返回引用需要特别注意的地方。

答案思考:

在Visual Studio上运行这段代码,输出结果是:


Add(1, 2) is :7

1

这个结果确实是未定义行为,但在某些情况下可能会输出7。之所以会出现这种情况,是因为Visual Studio的编译器在处理这种未定义行为时可能会做一些特殊的优化或处理,导致在某些环境下能够得到一个看似合理的结果。但这种行为是不可靠的,因为它依赖于具体的编译器实现细节。在不同的编译器或环境下,输出可能会完全不同。


正确的做法:是要么返回值,要么返回一个在调用者作用域内仍然存在的对象的引用。这样可以确保代码的行为是可预测和可移植的。

🌉引用和指针的区别

语法概念:


引用是变量的别名,没有独立的存储空间,而是和其引用的实体共用同一块内存空间。

指针是一个独立的变量,存储了另一个变量的内存地址。

声明语法:


引用使用&符号声明,如int& ref = x;

指针使用*符号声明,如int* ptr = &x;

操作方式:


引用直接访问和操作其引用的实体,如ref = 10;

指针需要先解引用(*)才能访问其指向的实体,如*ptr = 10;

Null值:


引用不能为空(Null),必须在声明时初始化为一个有效的实体。

指针可以为空(Null),指向空地址0x0。

让我们看看例子来说明引用和指针的区别:

假设我们有一个整型变量x,值为10。

使用引用:

int x = 10;
int& ref = x; // 声明引用ref,它是x的别名
ref = 20; // 通过ref修改x的值
cout << "x = " << x << endl; // 输出 x = 20

refx的引用,它们共享同一块内存空间。通过ref修改值,实际上是在修改x的值。 输出x的值为20,因为x的值已经被修改了。

使用指针:

int x = 10;
int* ptr = &x; // 声明指针ptr,存储x的地址
*ptr = 20; // 通过ptr解引用并修改x的值
cout << "x = " << x << endl; // 输出 x = 20
int

ptr是一个指向x的指针,存储了x的内存地址。通过*ptr解引用并修改值,实际上是在修改x的值。输出x的值为20,因为x的值已经被修改了。

底层实现上实际是有空间的,因为引用是按照指针方式来实现的

int main()
{
  int a = 10;
  int& ra = a;
  ra = 20;

  int* pa = &a;
  *pa = 20;
  return 0;
}

我们来看下引用和指针的汇编代码对比得出:在汇编中引用的底层逻辑还是指针,经过编译转换成汇编,还是进行指针的操作


引用和指针的不同点:


  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用比指针使用起来相对更安全

🌠常引用

从上述代码中,我们可以得出以下关于常引用的结论:

  1. 常量引用:
const int a = 10;
    //int& ra = a;   // 该语句编译时会出错,a为常量
    const int& ra = a;

对于常量对象a,我们可以使用常引用const int& ra = a;来引用它。这样做可以避免对常量进行修改,直接使用非常引用int& ra = a;会在编译时报错,因为不允许对常量进行非常引用。

2. 字面量常引用:

// int& b = 10; // 该语句编译时会出错,b为常量

const int& b = 10;

我们可以使用常引用const int& b = 10;来引用字面量常量。这样做可以避免创建临时变量, 直接使用非常引用int& b = 10;会在编译时报错,因为字面量不能被非常引用。

3. 类型不匹配的常引用:

double d = 12.34;
//int& rd = d; // 该语句编译时会出错,类型不同
const int& rd = d;

根据类型不同的变量,如double d = 12.34;,我们可以使用常引用const int& rd = d;来引用它,直接使用非常引用int& rd = d;会在编译时报错,因为类型不匹配。

相关文章
|
5月前
|
存储 安全 C++
C++中的引用和指针:区别与应用
引用和指针在C++中都有其独特的优势和应用场景。引用更适合简洁、安全的代码,而指针提供了更大的灵活性和动态内存管理的能力。在实际编程中,根据需求选择适当的类型,能够编写出高效、可维护的代码。理解并正确使用这两种类型,是掌握C++编程的关键一步。
74 1
|
4月前
|
存储 安全 C++
浅析C++的指针与引用
虽然指针和引用在C++中都用于间接数据访问,但它们各自拥有独特的特性和应用场景。选择使用指针还是引用,主要取决于程序的具体需求,如是否需要动态内存管理,是否希望变量可以重新指向其他对象等。理解这二者的区别,将有助于开发高效、安全的C++程序。
32 3
|
4月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
【7月更文挑战第28天】在 Android 开发中, NDK 让 Java 与 C++ 混合编程成为可能, 从而提升应用性能。**为何选 NDK?** C++ 在执行效率与内存管理上优于 Java, 特别适合高性能需求场景。**环境搭建** 需 Android Studio 和 NDK, 工具如 CMake。**JNI** 构建 Java-C++ 交互, 通过声明 `native` 方法并在 C++ 中实现。**实战** 示例: 使用 C++ 计算斐波那契数列以提高效率。**总结** 混合编程增强性能, 但增加复杂性, 使用前需谨慎评估。
140 4
|
4月前
|
Rust 安全 编译器
Rust与C++的区别及使用问题之Rust中的bound check对性能产生影响的问题如何解决
Rust与C++的区别及使用问题之Rust中的bound check对性能产生影响的问题如何解决
|
4月前
|
存储 自然语言处理 编译器
【C++入门 三】学习C++缺省参数 | 函数重载 | 引用
【C++入门 三】学习C++缺省参数 | 函数重载 | 引用
|
5月前
|
存储 安全 编译器
【C++航海王:追寻罗杰的编程之路】引用、内联、auto关键字、基于范围的for、指针空值nullptr
【C++航海王:追寻罗杰的编程之路】引用、内联、auto关键字、基于范围的for、指针空值nullptr
66 5
|
5月前
|
C++
C++引用
C++引用
|
5月前
|
存储 安全 编译器
【C++入门】—— C++入门 (中)_引用
【C++入门】—— C++入门 (中)_引用
36 5
|
5月前
|
C语言 C++ 编译器
【C++语言】冲突-C语言:输入输出、缺省参数、引用、内联函数
【C++语言】冲突-C语言:输入输出、缺省参数、引用、内联函数
【C++语言】冲突-C语言:输入输出、缺省参数、引用、内联函数