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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Toggle)

组件提供勾选框样式、状态按钮样式及开关样式。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 仅当ToggleType为Button时可包含子组件。 接口 Toggle(options: { type: ToggleType, is…

sparksql简介

什么是sparksql sparksql是一个用来处理结构话数据的spark模块&#xff0c;它允许开发者便捷地使用sql语句的方式来处理数据&#xff1b;它是用来处理大规模结构化数据的分布式计算引擎&#xff0c;其他分布式计算引擎比较火的还有hive&#xff0c;map-reduce方式。 sparksql…

mysql查询条件包含IS NULL、IS NOT NULL、!=、like %* 、like %*%,不能使用索引查询,只能使用全表扫描,是真的吗???

不知道是啥原因也不知道啥时候, 江湖上流传着这么一个说法 mysql查询条件包含IS NULL、IS NOT NULL、!、like %* 、like %*%,不能使用索引查询&#xff0c;只能使用全表扫描。 刚入行时我也是这么认为的&#xff0c;还奉为真理&#xff01; 但是时间工作中你会发现还是走索引…

bitset详解

本文旨在讲解位图的原理&#xff0c;以及位图有什么作用&#xff0c;如何实现位图。希望读完本篇文章能对小伙伴们有一定的收获&#xff01;上干货&#xff01; 什么是位图 位图就是bitmap的缩写&#xff0c;所谓bitmap&#xff0c;就是用每一位来存放某种状态&#xff0c;适用…

代理IP品质对Tik Tok代理的重要性

随着Tik Tok的迅速崛起&#xff0c;越来越多的人开始关注如何透过Tik Tok进行行销和推广。其中&#xff0c;使用Tik Tok代理程式是常见的方法。 然而&#xff0c;在选择和使用代理时&#xff0c;IP品质是一个不可忽视的因素。本文将探讨IP品质对Tik Tok代理的重要性&#xff0…

架起桥梁,畅享流通:如何使用私有Registry实现镜像跨源同步与管理

在Docker容器化世界中&#xff0c;镜像作为构建和运行应用的基础单元&#xff0c;其管理与分发对于企业级应用至关重要。私有Registry作为存储和管理Docker镜像的中心仓库&#xff0c;不仅可以保障企业数据的安全性&#xff0c;更能通过镜像同步功能实现跨源镜像的统一管理和高…

R语言中的常用基础绘图函数 直方图,箱线图,条形图,散点图

目录 R语言中的绘图参数 绘图函数 1.plot函数绘制散点图 2.hist函数绘制直方图 如何修饰直方图? 如何在直方图上标注各组频数&#xff1f; 使用text函数把某些信息标注在直方图上 如何在直方图上添加概率密度曲线&#xff1f; 3.boxplot函数绘制箱线图 4.barplot函数…

代码随想录算法训练营 DAY 14 | 二叉树的递归遍历和迭代遍历

二叉树基础 种类 满二叉树&#xff1a;深度为k&#xff0c;有2^k-1个节点的二叉树 完全二叉树&#xff1a;除了最底层可能没满&#xff0c;且都在靠左侧 优先级队列其实是一个堆&#xff0c;堆就是一棵完全二叉树&#xff0c;同时保证父子节点的顺序关系。 二叉搜索树&…

中间件-消息队列

消息队列基础知识 什么是消息队列 本处提到的消息队列是指各个服务以及系统组件/模块之间的通信&#xff0c;属于一种中间件。参与消息传递的双方称为生产者和消费者&#xff0c;生产者负责发送消息&#xff0c;消费者负责处理消息。 消息队列作用 通过异步处理&#xff0…

git的起源

开篇一张图&#xff1a; 开源项目linux kernel开发&#xff0c;参与开发与维护者众多。1991至2005年期间绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上。 在2002 年&#xff0c;整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维…

JavaScript中new操作符具体干了什么

文章目录 一、是什么二、流程三、手写new操作符 一、是什么 在JavaScript中&#xff0c;new操作符用于创建一个给定构造函数的实例对象 例子 function Person(name, age){this.name name;this.age age; } Person.prototype.sayName function () {console.log(this.name) …

【LIMS】微服务

目录 一、服务解决方案-Spring Cloud Alibaba1.1选用原因&#xff08;基于Spring Cloud Alibaba的试用场景&#xff09;1.2 核心组件使用前期规划 部署 nacos部署 mino使用JavaFreemarker模板引擎&#xff0c;根据XML模板文件生成Word文档使用JavaFlowable 工作流引擎前端 -vue…

前端项目,个人笔记(三)【Vue-cli - api封装-axios使用举例】

目录 前言 1、axios配置与测试 1.1、配置 1.2、测试 2、使用axios案例-渲染header 3、Pinia优化重复请求 3.1、为什么&#xff1f; 3.2、使用Pinia优化代码步骤 步骤一&#xff1a;在main.js中创建 Pinia 实例&#xff0c;并将其作为插件添加到 Vue 应用中 步骤二&am…

golang面试题总结

零、go与其他语言 0、什么是面向对象 在了解 Go 语言是不是面向对象&#xff08;简称&#xff1a;OOP&#xff09; 之前&#xff0c;我们必须先知道 OOP 是啥&#xff0c;得先给他 “下定义” 根据 Wikipedia 的定义&#xff0c;我们梳理出 OOP 的几个基本认知&#xff1a; …

sparksession对象简介

什么是sparksession对象 spark2.0之后&#xff0c;sparksession对象是spark编码的统一入口对象&#xff0c;通常我们在rdd编程时&#xff0c;需要SparkContext对象作为RDD编程入口&#xff0c;但sparksession对象既可以作为RDD编程对象入口&#xff0c;在sparkcore编程中可以通…

Linux 建立链接(ln)

目录 1、ln命令 创建软链接&#xff1a; 创建硬链接&#xff1a; 2、输出重定向&#xff08;>/>>&#xff09; 3、管道&#xff08;|&#xff09; 1、ln命令 &#xff08;英文全拼&#xff1a;link files&#xff09;为某一个文件在另外一个位置建立一个同步的…

数据结构奇妙旅程之红黑树

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

SpringBoot集成Jasypt实现敏感信息加密

项目场景&#xff1a; 在服务中不可避免的需要使用到一些秘钥&#xff08;数据库、redis等&#xff09;开发和测试环境还好&#xff0c;但生产如果采用明文配置将会有安全问题&#xff0c;jasypt是一个通用的加解密库&#xff0c;可以使用它。 在Spring Boot中使用Jasypt加密和…

nfs介绍与配置

NFS 1. nfs简介 nfs特点 NFS&#xff08;Network File System&#xff09;即网络文件系统&#xff0c;是FreeBSD支持的文件系统中的一种&#xff0c;它允许网络中的计算机之间通过TCP/IP网络共享资源在NFS的应用中&#xff0c;本地NFS的客户端应用可以透明地读写位于远端NFS服…

CTF题型 SSTI(2) Flask-SSTI典型题巩固

CTF题型 SSTI(2) Flask-SSTI典型题巩固 文章目录 CTF题型 SSTI(2) Flask-SSTI典型题巩固前记1.klf__sstiSSTI_Fuzz字典&#xff08;网上收集自己补充&#xff09; 2.klf_2数字问题如何解决了&#xff1f;|count |length都被禁&#xff1f; 3.klf_3 前记 从基础到自己构造paylo…
最新文章