当前位置: 首页 > article >正文

C++ 引用变量、引用形参

C++ 引用变量、引用形参

引用变量

在C++中,引用变量是对另一个变量的别名。引用被创建时必须被初始化,且一旦被初始化指向某个变量后,就不能改变指向其他变量。引用的主要用途包括作为函数参数,以达到传递引用(传引用)的效果,从而允许函数修改调用者的数据,或者用于返回函数中的多个值。

基本语法和特性

创建一个引用时,你需要在变量类型后面加上&符号。例如,int& myRef = myVar;声明了一个对整型变量myVar的引用myRef

int main() {
    int original = 30;
    int& ref = original; // ref是original的一个引用

    ref = 20; // 修改引用也就是修改了original
    std::cout << original; // 输出20,因为original被通过引用ref修改了
}

引用的特性

  • 直接访问:引用提供了被引用变量的直接访问。修改引用即修改原始变量。
  • 必须初始化:引用在创建时必须初始化,且之后不能更改为引用其他变量。
  • 无NULL引用:引用必须连接到合法的存储单元,不能像指针那样为NULL。
  • 自动解引用:与指针不同,引用使用时不需要解引用操作,它们的使用就像是普通变量一样。

引用作为函数参数

引用常用于函数参数,使得函数能够修改传入的参数值。

void increment(int& value) {
    value += 1;
}

int main() {
    int num = 5;
    increment(num);
    std::cout << num; // 输出6
}

在这个例子中,increment函数接受一个整数的引用,函数内对引用的修改直接影响到原始变量num

引用作为函数返回值

引用也可以作为函数的返回类型,但要注意不要返回局部变量的引用,因为局部变量在函数返回时会被销毁,其引用将指向一个已经不存在的内存位置。

int& getMax(int& a, int& b) {
    return (a > b) ? a : b;
}

int main() {
    int x = 5, y = 10;
    getMax(x, y) = 15; // 实际上修改了y的值
    std::cout << y; // 输出15
}

102 1#include 2#include 3using namespace std;4​5// 定义一个Student类用于存储学生信息6class Student {7 string name; // 学生姓名8 double score; // 学生分数9​10public:11 // 构造函数,允许带默认值,便于创建无特定信息的学生对象12 Student(string n=“no”, double s=33.3) {13 name = n;14 score = s;15 }16​17 // 声明友元函数,允许非成员函数访问私有成员18 // 重载<<运算符以便于直接输出Student对象19 friend ostream& operator<<(ostream& o, Student s);20};21​22// 实现重载的<<运算符23ostream& operator<<(ostream& o, Student s) {24 cout << s.name << “,” << s.score << endl; // 输出学生的姓名和分数,然后换行25 return o; // 返回ostream对象,支持链式调用26}27​28// 定义一个模板类Vector,可用于存储任意类型的动态数组29template30class Vector {31 T* data; // 指向数组的指针32 int capacity; // 数组的容量33 int n; // 数组当前存储的元素数量34​35public:36 // 构造函数,初始化数组的容量和大小37 Vector(int cap=3) {38 data = new T[cap]; // 动态分配存储空间39 if (data == 0) { // 如果分配失败,则设置容量和大小为040 cap = 0;41 n = 0;42 return;43 }44 capacity = cap; // 设置容量45 n = 0; // 初始化元素数量为046 }47​48 // 向数组末尾添加一个元素49 void push_back(T e) {50 if (n == capacity) { // 如果当前大小已达容量51 cout << “增加容量” << endl; // 输出提示信息52 T* p = new T[2 * capacity]; // 申请新的两倍大小的数组53 if § {54 for (int i = 0; i < n; i++)55 p[i] = data[i]; // 复制旧数据到新数组56 delete[]data; // 释放旧数组57 data = p; // 更新指针指向新数组58 capacity = 2 * capacity; // 更新容量59 }60 else {61 return; // 如果新数组分配失败,直接返回62 }63 }64 data[n] = e; // 将新元素添加到数组末尾65 n++; // 元素数量增加66 }67​68 // 重载[]运算符,允许通过下标访问数组元素69 T operator[](int i)const {70 if (i < 0 || i >= n)throw"下标非法"; // 如果下标越界,抛出异常71 return data[i]; // 返回下标对应的元素72 }73​74 // 返回数组当前存储的元素数量75 int size() {76 return n;77 }78};79​80int main() {81 // 创建一个Vector对象,用于存储Student对象82 Vector v;83 // 向Vector中添加Student对象84 v.push_back(Student(“Li”,45.7));85 v.push_back(Student(“Ai”, 45.7));86 v.push_back(Student(“Bi”, 45.7));87 // 遍历Vector并输出每个Student对象88 for (int i = 0; i < v.size(); i++)89 cout << v[i];90 cout << endl;91​92 // 继续向Vector中添加Student对象93 v.push_back(Student(“Liu”, 45.7));94 v.push_back(Student(“Lsi”, 45.7));95 // 再次遍历Vector并输出每个Student对象96 for (int i = 0; i < v.size(); i++)97 cout << v[i];98 cout << endl;99​100 return 0;101}102​C++

  • 语法差异:引用使用更简洁的语法,不需要使用解引用操作符*来访问值。
  • 可变性:指针可以在任何时候改变指向另一个对象,而引用一旦绑定到一个对象,就不能改变指向。
  • 安全性:引用比指针更安全,因为引用保证指向一个有效的存储单元。

总结

引用是C++中强大的特性,提供了一种安全且易于使用的方式来操作其他变量。它们在函数参数传递、返回值、以及需要直接操作调用者数据时特别有用。然而,使用引用时需要谨慎,特别是作为函数返回值时,确保不会返回局部变量的引用。

代码示例

#include <iostream> // 包含标准输入输出流库
using namespace std; // 使用标准命名空间std,允许直接使用cout等

int main() {
    int a = 3, &r = a; // 定义一个整型变量a并初始化为3,定义r为a的引用
    cout << a << '\t' << r << endl; // 输出a和r的值,这时两者都是3,之间用制表符分隔

    r = 5; // 通过引用r修改a的值为5
    cout << a << '\t' << r << endl; // 再次输出a和r的值,此时两者都变为5

    return 0; // 程序正常结束
}

函数的值形参

在C++中,当函数参数以值传递(Value Semantics)的方式被传入时,该参数被称为值形参(Value Parameter)。这意味着函数接收的是实参(调用函数时传入的参数)的一个副本。因此,函数内对该形参的任何修改都不会影响到原始实参。这种参数传递方式简单、直观,适用于基本数据类型如整数、浮点数、字符等,以及当你不希望函数修改传入数据时。

值形参的特点

  1. 隔离性:值形参提供了一种方式,确保函数内的操作不会对外部变量产生副作用。这种隔离性使得代码更易于理解和维护。
  2. 副本开销:对于较大的对象,如自定义类实例或大型容器,通过值传递会产生额外的副本开销,可能影响性能。在这种情况下,通过引用或指针传递可能更合适。
  3. 适用场景:值传递特别适合于处理基本数据类型和不需要修改原始数据的场景。

示例

下面是一个使用值形参的示例,展示了函数如何接收实参的副本并进行操作:

#include <iostream>
using namespace std;

void addOne(int value) {
    value += 1; // 这里修改的是value的副本,不影响原始变量
    cout << "Inside function: " << value << endl;
}

int main() {
    int num = 3;
    addOne(num); // num作为实参传入,函数内部操作的是num的副本
    cout << "In main: " << num << endl; // num的值不变,因为函数操作的是副本

    return 0;
}

输出:

Inside function: 4
In main: 3

这个例子中,addOne函数接收一个整型值形参value,尽管函数内部value的值被修改,但这个修改不会影响到main函数中的num变量,因为valuenum的一个副本。

值传递与引用传递

值传递与引用(或指针)传递是C++中两种基本的参数传递方式。与值传递不同,引用或指针传递允许函数直接操作原始数据,而不是其副本。选择哪种方式取决于具体的需求:

  • 如果你希望函数内的修改反映到原始数据上,或者想避免大对象的副本开销,应使用引用或指针传递。
  • 如果你希望保护原始数据不被修改,或者处理的数据量不大,值传递是一个简单且有效的选择。

总之,值形参在C++函数参数传递中扮演了重要角色,尤其是在需要保证数据不被修改或仅操作小型数据时。

示例代码

using namespace std; // 使用标准命名空间,允许直接访问std中的元素,如cout

// 定义一个交换两个整数值的函数,参数通过值传递方式传入
void swap(int x, int y) {
    cout << x << '\t' << y << endl; // 首先打印函数接收到的两个参数的值
    int t = x; // 使用临时变量t保存x的值
    x = y; // 将y的值赋给x
    y = t; // 将临时变量t的值(即原来x的值)赋给y
    cout << x << '\t' << y << endl; // 打印交换后的x和y的值
}

int main() {
    int a = 3, b = 4; // 定义并初始化两个整数变量a和b
    cout << a << '\t' << b << endl; // 打印变量a和b的初始值
    swap(a, b); // 调用swap函数尝试交换a和b的值
    cout << a << '\t' << b << endl; // 再次打印a和b的值,检查它们是否被交换
}

函数的值形参:传递指针

在C++中,通过指针传递参数(传递指针作为函数的值形参)是一种使函数能够访问和修改调用者中变量的有效方法,尤其是对于数组、大型结构体或类实例等较大的数据结构。这种方法通过将变量的地址传递给函数实现,允许函数通过指针间接访问和修改原始数据。

基本语法和概念

在C++中,指针是一种特殊的变量,用于存储另一个变量的内存地址。当你将指针作为函数的参数传递时,实际上传递的是指向数据的指针的值(即内存地址),而不是数据本身的副本。这允许函数通过解引用指针来访问和修改存储在该地址的数据。

定义接受指针参数的函数

函数定义时,参数类型后面加上*表示该参数是一个指针:

void modify(int* ptr) {
    *ptr = 10; // 通过指针修改指向的值
}
调用函数时传递指针

调用接受指针参数的函数时,需要传递变量的地址,使用&操作符:

int main() {
    int num = 5;
    modify(&num); // 传递num的地址
    std::cout << num; // 输出10
}

使用指针参数的好处

  • 效率:对于大型数据结构,使用指针传递避免了复制整个数据结构的开销。
  • 灵活性:函数可以修改调用者中的数据,包括数组和对象。
  • 功能性:通过指针,函数能够处理动态分配的内存和数组,以及返回多个值。

注意事项

  • 空指针:在函数内部使用指针之前,应检查其是否为nullptr(空指针),以避免解引用空指针导致运行时错误。
  • 野指针:确保传递给函数的指针指向有效的内存。未初始化的指针可能指向任意位置,导致不可预测的行为。
  • 所有权和生命周期:函数通过指针操作的数据必须保证在函数操作期间其内存是有效的。特别是对于动态分配的内存,需要明确所有权和释放责任,避免内存泄露或重复释放。

与引用形参的比较

  • 语法差异:引用形参通常更简洁,不需要在调用时使用&或在函数内部使用*进行解引用。
  • 空值:指针可以为nullptr,而引用必须绑定到有效的对象。
  • 可选性:指针可以通过nullptr表示“无数据”或“可选”的参数,而引用总是假定关联到一个有效对象。

总的来说,通过指针传递参数为C++程序提供了强大的灵活性和控制能力,尤其是在需要操作大型数据或需要函数直接修改调用者中数据时。然而,这也要求开发者更加注意指针的正确使用和相关的安全问题。

代码示例

#include <iostream> // 包含标准输入输出流库
using namespace std; // 使用标准命名空间std

// 定义一个交换两个整数值的函数,参数为两个整数的指针
void swap(int* x, int* y) {
    int t = *x; // 使用临时变量t保存x指向的值
    *x = *y;    // 将y指向的值赋给x指向的变量
    *y = t;     // 将t(原x指向的值)赋给y指向的变量
}

int main() {
    int a = 3, b = 4; // 定义并初始化两个整数变量a和b
    cout << a << '\t' << b << endl; // 打印变量a和b的初始值
    swap(&a, &b); // 调用swap函数,传入a和b的地址
    cout << a << '\t' << b << endl; // 再次打印a和b的值,检查它们是否被交换
}

引用形参:引用实参

在C++中,使用引用形参(Reference Parameter)传递引用实参(Reference Argument)是一种允许函数直接操作调用者中的变量而不创建其副本的方法。这种机制通过在函数定义时在参数类型后面添加&符号来实现,使得函数参数成为原始变量的一个别名。因此,对形参的任何操作都会反映在实参上,实现了数据的双向传递。

引用形参的基本使用

引用形参使得函数能够直接访问和修改调用者提供的变量。这不仅提高了效率(避免了不必要的数据复制),而且使得函数能够返回多个结果。

示例:交换两个变量的值
#include <iostream>
using namespace std;

// 交换两个整数的值的函数,参数为引用形参
void swap(int &x, int &y) {
    int temp = x;
    x = y;
    y = temp;
}

int main() {
    int a = 10, b = 20;
    cout << "Before swap: a = " << a << ", b = " << b << endl;
    swap(a, b); // 调用swap时,a和b作为引用传递
    cout << "After swap: a = " << a << ", b = " << b << endl;
    return 0;
}

在这个例子中,swap函数通过引用形参接收两个整数变量,对这些变量进行交换。调用swap(a, b)后,ab的值在函数外部也发生了变化,因为它们直接被函数操作。

引用形参的优点

  • 效率:对于大型对象(如大型结构体或类实例),使用引用传递避免了复制整个对象的开销。
  • 简洁性:引用形参允许函数直接操作数据,使得函数调用和定义更加简洁。
  • 灵活性:可以安全地返回函数内部创建的对象的引用,前提是这些对象在函数外部依然有效(比如静态局部变量或者在堆上分配的对象)。

注意事项

  • 生命周期:返回局部对象的引用是危险的,因为函数结束后局部对象会被销毁,其引用将变得无效。
  • 一致性:函数通过引用形参修改实参的能力应谨慎使用,以避免意外修改导致的错误或不一致。
  • 默认行为:引用形参使得函数的默认行为变为可修改实参,这与值传递形成对比,在设计接口时应明确意图。

结论

引用形参是C++中一种强大的特性,提供了一种有效、灵活的方式来操作函数调用者的数据。它们特别适用于需要修改传入数据或避免大型对象复制开销的场景。然而,使用时需要注意保证引用的有效性和避免不必要的数据修改。

代码示例

#include <iostream> // 包含标准输入输出流库
using namespace std; // 使用标准命名空间std,允许直接访问cout等

// 定义一个交换两个整数值的函数,参数为两个整数的引用
void swap(int &x, int &y) {
    int t = x; // 使用临时变量t保存x的值
    x = y;    // 将y的值赋给x
    y = t;     // 将t(原x的值)赋给y
}

int main() {
    int a = 3, b = 4; // 定义并初始化两个整数变量a和b
    cout << a << '\t' << b << endl; // 打印变量a和b的初始值
    swap(a, b); // 调用swap函数,传入a和b作为引用
    cout << a << '\t' << b << endl; // 再次打印a和b的值,检查它们是否被交换
}

`

http://www.kler.cn/a/274194.html

相关文章:

  • springboot/ssm私房菜定制上门服务系统Java代码编写web厨师上门做菜
  • STM32串口第一次接收数据时第一个字节丢失的问题
  • 高强度螺栓等级划分
  • Ubuntu下ESP32-IDF开发环境搭建
  • nmap扫描优化
  • GPU环境配置
  • 鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Toggle)
  • 高效日志为服务器保驾护航
  • sparksql简介
  • mysql查询条件包含IS NULL、IS NOT NULL、!=、like %* 、like %*%,不能使用索引查询,只能使用全表扫描,是真的吗???
  • bitset详解
  • 代理IP品质对Tik Tok代理的重要性
  • Vue快速教程:如何优雅地移除数组中的特定元素?
  • 架起桥梁,畅享流通:如何使用私有Registry实现镜像跨源同步与管理
  • linux系统中的PS命令详解
  • R语言中的常用基础绘图函数 直方图,箱线图,条形图,散点图
  • 深入理解nginx的请求限速模块[下]
  • 代码随想录算法训练营 DAY 14 | 二叉树的递归遍历和迭代遍历
  • 中间件-消息队列
  • git的起源
  • JavaScript中new操作符具体干了什么
  • 【LIMS】微服务
  • 前端项目,个人笔记(三)【Vue-cli - api封装-axios使用举例】
  • c++ 自己实现一个迭代器
  • golang面试题总结
  • sparksession对象简介