C++开发基础之函数的参数类型高级用法

前言

当涉及到高级参数传递方式时,有几种常见的技术可以在 C++ 中使用。下面是对每种技术的详细介绍,并附带示例说明:

1. 指针的引用(Reference to Pointer)

指针的引用允许在函数内部修改指针本身,而不仅仅是指针所指向的值。通过将指针作为引用传递给函数,可以直接修改指针的值。

示例:

void modifyPointer(int*& ptr) {int* newPtr = new int(10);delete ptr;ptr = newPtr;
}int main() {int* ptr = new int(5);modifyPointer(ptr);// 现在 ptr 指向一个新的动态分配的整数delete ptr;return 0;
}

案例分析

使用指针的引用可以方便地实现两个指针的交换,以下是一个示例代码:

#include <iostream>void swapPointers(int* &ptr1, int* &ptr2) {int* temp = ptr1;ptr1 = ptr2;ptr2 = temp;
}int main() {int a = 42, b = 100;int* pa = &a, * pb = &b;std::cout << "Before swap: pa=" << pa << ", pb=" << pb << std::endl;swapPointers(pa, pb);std::cout << "After swap: pa=" << pa << ", pb=" << pb << std::endl;return 0;
}

在上面的示例中,我们定义了两个整数 ab,并分别定义了指向它们的指针 papb。然后我们调用 swapPointers 函数来交换两个指针的值,并输出结果。

输出结果是:

Before swap: pa=0x7ffc5fde8e34, pb=0x7ffc5fde8e38
After swap: pa=0x7ffc5fde8e38, pb=0x7ffc5fde8e34

这个示例展示了如何使用指针的引用来交换两个指针的值,从而达到交换指针指向的效果。

冒泡排序

当然可以使用指针和指针的引用来实现冒泡排序算法。在冒泡排序算法中,我们需要比较相邻的元素,并根据需要交换它们的位置。通过使用指针和指针的引用,我们可以更高效地操作数组中的元素。

以下是一个示例代码,演示了如何使用指针和指针的引用来实现冒泡排序算法:

#include <iostream>void bubbleSort(int* arr, int size) {for (int i = 0; i < size - 1; ++i) {for (int j = 0; j < size - i - 1; ++j) {if (*(arr + j) > *(arr + j + 1)) {std::swap(*(arr + j), *(arr + j + 1));}}}
}int main() {int arr[] = {64, 34, 25, 12, 22, 11, 90};int size = sizeof(arr) / sizeof(arr[0]);std::cout << "Before sorting:" << std::endl;for (int i = 0; i < size; ++i) {std::cout << arr[i] << " ";}std::cout << std::endl;bubbleSort(arr, size);std::cout << "After sorting:" << std::endl;for (int i = 0; i < size; ++i) {std::cout << arr[i] << " ";}std::cout << std::endl;return 0;
}

在这个示例中,我们定义了一个整数数组 arr,并使用指针 arr 来传递数组的地址给 bubbleSort 函数。在 bubbleSort 函数中,我们使用指针来访问数组中的元素,并进行比较和交换操作。

输出结果应该是:

Before sorting:
64 34 25 12 22 11 90 
After sorting:
11 12 22 25 34 64 90 

这个示例展示了如何使用指针和指针的引用来实现冒泡排序算法。

2. 右值引用(Rvalue References)

在 C++11 中引入了右值引用(rvalue reference)的概念,它是对左值引用(lvalue reference)的补充。右值引用可以绑定到右值(例如临时对象、字面量或表达式的结果),而左值引用只能绑定到左值(例如变量、数组元素或对象成员)。右值引用的主要目的是实现移动语义和完美转发。

在 C++ 中,一个表达式要么是左值,要么是右值。简单来说,左值是可以取地址的表达式,而右值是不可取地址的表达式。例如,一个变量就是一个左值,可以通过取地址符 & 来获取它的地址;而一个常量或表达式的结果就是一个右值,不能使用取地址符来获取它的地址。

下面是一个示例代码,演示了如何定义和使用右值引用:

#include <iostream>void printValue(int &&value) {std::cout << "Right value: " << value << std::endl;
}int main() {int a = 42;printValue(a); // 编译错误,a 是一个左值printValue(100); // 正确,100 是一个右值return 0;
}

在上面的示例中,我们定义了一个函数 printValue,它接受一个右值引用作为参数并打印该值。在 main 函数中,我们尝试将一个左值 a 传递给 printValue 函数,但编译器会给出错误提示。然后我们将一个字面量值 100 传递给 printValue 函数,这是一个右值,并且可以被正确地打印出来。

案例分析

右值引用还可以用于实现移动语义和完美转发,右值引用允许将临时对象(右值)的所有权转移给另一个对象,避免不必要的数据拷贝。通过使用 && 定义右值引用,可以实现移动语义。

示例:

class MyString {
public:MyString() : m_data(nullptr), m_size(0) {}MyString(const char* str) {m_size = std::strlen(str);m_data = new char[m_size + 1];std::strcpy(m_data, str);}// 移动构造函数MyString(MyString&& other) noexcept {m_size = other.m_size;m_data = other.m_data;other.m_size = 0;other.m_data = nullptr;}// 移动赋值运算符MyString& operator=(MyString&& other) noexcept {if (this != &other) {delete[] m_data;m_size = other.m_size;m_data = other.m_data;other.m_size = 0;other.m_data = nullptr;}return *this;}~MyString() {delete[] m_data;}private:char* m_data;std::size_t m_size;
};int main() {MyString str1("Hello");MyString str2 = std::move(str1); // 使用 std::move 将 str1 的所有权转移给 str2return 0;
}

3. 完美转发(Perfect Forwarding)

完美转发(Perfect Forwarding)是指在函数模板中保持参数的原始类型(包括左值和右值属性)进行参数传递的能力。通过完美转发,函数可以将它接收到的参数原封不动地传递给另一个函数,而不会丢失参数的左值或右值属性。

在 C++11 引入右值引用之后,完美转发成为可能。通过使用 std::forward 函数模板,可以实现完美转发。std::forward 函数模板位于 <utility> 头文件中,并可以将参数转发为右值或左值引用,具体取决于原始参数的类型。

下面是一个示例代码,演示了如何使用完美转发:

#include <iostream>
#include <utility>void process(int& value) {std::cout << "Lvalue reference: " << value << std::endl;
}void process(int&& value) {std::cout << "Rvalue reference: " << value << std::endl;
}template <typename T>
void forwardValue(T&& value) {process(std::forward<T>(value));
}int main() {int a = 42;forwardValue(a); // 传递左值forwardValue(100); // 传递右值return 0;
}

在上面的示例中,我们定义了两个重载函数 process,分别接受左值引用和右值引用作为参数,并打印出不同类型的引用。然后,我们定义了一个函数模板 forwardValue,它使用 std::forward 来保持参数的原始类型并调用 process 函数。在 main 函数中,我们分别传递一个左值和一个右值给 forwardValue 函数,以演示完美转发的效果。

通过完美转发,我们可以在函数模板中正确地传递参数的左值或右值属性,从而实现更加灵活和高效的参数传递方式。

案例分析

完美转发是一种在模板函数中传递参数的技术,可以保持参数的值类别(左值
或右值)不变,并将参数原封不动地传递给其他函数。通常与模板和右值引用一起使用。

示例:

template <typename T>
void forwardValue(T&& value) {someFunction(std::forward<T>(value));
}void someFunction(int& value) {// 处理左值的情况
}void someFunction(int&& value) {// 处理右值的情况
}int main() {int x = 42;forwardValue(x); // 调用 someFunction(int& value)forwardValue(10); // 调用 someFunction(int&& value)return 0;
}

4. 可变参数模板(Variadic Templates)

可变参数模板(Variadic Templates)是 C++11 引入的一个特性,它允许定义接受任意数量参数的函数模板或类模板。通过可变参数模板,我们可以编写更加灵活和通用的代码,能够处理不确定数量的参数。

在可变参数模板中,我们使用 ...(省略号)表示接受任意数量参数的部分。这样,我们可以在模板参数列表中包含一个或多个参数,并使用递归、展开等技术来处理这些参数。

下面是一个示例代码,演示了如何使用可变参数模板:

#include <iostream>// 递归终止条件:当没有参数时,打印换行并返回
void printValues() {std::cout << std::endl;
}// 可变参数模板,打印所有参数的值
template <typename T, typename... Args>
void printValues(T value, Args... args) {std::cout << value << " ";printValues(args...); // 递归调用,处理剩余参数
}int main() {printValues(1, 2.5, "Hello", true);return 0;
}

在上面的示例中,我们定义了两个函数模板 printValues。第一个函数模板是递归终止条件,当没有参数时,打印换行并返回。第二个函数模板是可变参数模板,它接受一个参数 value,打印它的值,并递归调用 printValues 处理剩余的参数。

main 函数中,我们调用 printValues 并传递四个不同类型的参数:整数、浮点数、字符串和布尔值。通过可变参数模板的递归调用,所有的参数都会被打印出来。

可变参数模板使得我们能够编写更加通用和灵活的代码,可以处理不确定数量的参数,并且可以在递归、展开等场景下发挥重要作用。

案例分析

可变参数模板允许定义可以接受任意数量和类型参数的模板函数或类,
用于实现泛型编程时非常有用。

示例:

template <typename... Args>
void printArgs(Args&&... args) {(std::cout << ... << args) << std::endl;
}int main() {printArgs(1, "hello", 3.14); // 输出:1hello3.14return 0;
}

这些高级参数传递方式在 C++ 中被广泛使用,可以提供更大的灵活性和性能优化。使用这些技术,可以编写出更为通用、高效的代码。

结论

高级参数传递方式包括指针的引用、右值引用、完美转发和可变参数模板,它们分别对应于 C++ 中不同的特性。

  • 指针的引用(Reference to Pointer)是一种将指针作为参数传递并修改指针本身的方式。指针的引用通常在函数中使用,通过将指针参数声明为指针的引用,可以修改指针的值,从而实现对指针的间接修改。指针的引用的结论是:可以通过指针的引用来修改指针本身的值,而不是指针所指向的对象。

  • 右值引用(Rvalue References)是一种引用右值的方式。右值引用通常在函数中使用,通过将参数声明为右值引用,可以将右值绑定到引用上,从而实现对右值的使用或转移。右值引用的结论是:可以通过右值引用来实现移动语义,提高代码的效率和性能。

  • 完美转发(Perfect Forwarding)是一种保留函数调用时参数的左值或右值属性的方式。它通常在函数模板中使用,通过 std::forward 函数模板将参数从一个函数转发到另一个函数,从而实现参数类型的保留和转移。完美转发的结论是:可以通过完美转发来实现通用的函数模板,支持不同类型和左值/右值属性的参数传递。

  • 可变参数模板(Variadic Templates)是一种定义接受任意数量参数的函数模板或类模板的方式。它允许在模板参数列表中包含一个或多个参数,并使用递归、展开等技术来处理这些参数。可变参数模板的结论是:可以通过可变参数模板来处理不确定数量的参数,并在递归、展开等场景下发挥重要作用,实现各种复杂的功能。

这些高级参数传递方式为 C++ 中的泛型编程和元编程提供了更多可能性,使得我们能够编写更加通用、灵活的代码,并在编译时展开参数列表,实现各种复杂的功能。

总的来说,指针的引用、右值引用、完美转发和可变参数模板都是 C++ 中非常有用的特性,熟练掌握它们的使用将有助于提升代码的表达力和效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/693041.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BERT模型中的input_ids和attention_mask参数

一、概述 1.1 input_ids 在BERT模型及其衍生体中&#xff0c;输入文本首先经过一个分词处理流程&#xff0c;其中文本被细分为单词或子单词&#xff08;subwords&#xff09;&#xff0c;每个分词随后映射到一个唯一的整数标识符。这些标识符组成了所谓的input_ids数组&#x…

2、电源管理入门之开机详解

目录 1. 硬件上电 2. ATF运行 3. Linux启动 3.1 内核启动start_kernel 3.2 平台启动setup_arch 3.4 DTS初始化psci_dt_init 3.5 系统rest创建kernel_init线程 3.6 SMP初始化smp_init 3.7 PSCI接口psci_cpu_on 3.8 SMC返回secondary_entry 系统开机牵扯到:“我是…

记录人工智能领域的人、事、社区

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 目录 简述概要关注人物OpenAI团队成员 关注事件2015年2016年2017年2018年2019年2020年2021年2022年2023年2024年 关注社区OpenAITensorFlowPyTorchMIT Technology ReviewReddit Machine LearningIEEE Transactions on Neural Net…

java 使用documents4j将XML转为pdf文件的方式

1.背景&#xff1a; 通过spire.doc.free将word转换成PDF时存在缺陷&#xff1a;只能获取前3页。获取全文另外需支付费用。 2.解决办法 使用documents4j&#xff0c;documents4j会保留原word文件中更多的样式&#xff0c;如修订模式下的差异化字体颜色、文档右侧修订记录等。 …

信息学奥赛一本通1188:菲波那契数列(2)

1188&#xff1a;菲波那契数列(2) 时间限制: 1000 ms 内存限制: 65536 KB 提交数: 70272 通过数: 26790 【题目描述】 菲波那契数列是指这样的数列: 数列的第一个和第二个数都为1&#xff0c;接下来每个数都等于前面2个数之和。 给出一个正整数a&#xff0c;要求菲…

Android Studio基础(下载安装与简单使用)

1、搭建Android开发平台 1.1 Android Studio 下载地址及版本说明 Android 开发者官网&#xff1a; https://developer.android.com/index.html&#xff08;全球&#xff0c;需科学上网&#xff09; https://developer.android.google.cn/index.html&#xff08;国内&#xff…

oracle INSERT 批量插入写法

直接采用mysql那种INSERT批量插入语句的写法&#xff0c;会报 SQL 错误 [933] [42000]: ORA-00933: SQL 命令未正确结束 它可以使用以下方法来批量插入数据&#xff1a; 使用INSERT ALL语句&#xff1a;可以在一条INSERT语句中插入多个记录。使用这种方法&#xff0c;您可以为…

十三、集合进阶——单列集合 及 数据结构

单列集合 及 数据结构 13.1 集合体系结构13.1.2 单列集合1. Collection2.Collection 的遍历方式迭代器遍历增强for遍历Lambda表达式遍历 3.List集合List集合的特有方法List集合的遍历方式五种遍历方式对比 4.数据结构1).栈2).队列3&#xff09;数组4&#xff09;链表小结5&…

PyTorch – 逻辑回归

data 首先导入torch里面专门做图形处理的一个库&#xff0c;torchvision&#xff0c;根据官方安装指南&#xff0c;你在安装pytorch的时候torchvision也会安装。 我们需要使用的是torchvision.transforms和torchvision.datasets以及torch.utils.data.DataLoader 首先DataLoa…

Tomcat是如何打破“双亲委派“机制的

目录 一、什么是双亲委派机制? 二、什么情况下我们需要破坏双亲委派模型?

营销系统黑名单优化:位图的应用解析 | 京东云技术团队

背景 营销系统中&#xff0c;客户投诉是业务发展的一大阻碍&#xff0c;一般会过滤掉黑名单高风险账号&#xff0c;并配合频控策略&#xff0c;来减少客诉&#xff0c;进而增加营销效率&#xff0c;减少营销成本&#xff0c;提升营销质量。 营销系统一般是通过大数据分析建模…

2024年了,如何从 0 搭建一个 Electron 应用

简介 Electron 是一个开源的跨平台桌面应用程序开发框架&#xff0c;它允许开发者使用 Web 技术&#xff08;如 JavaScript、HTML 和 CSS&#xff09;来构建桌面应用程序。Electron 嵌入了 Chromium&#xff08;一个开源的 Web 浏览器引擎&#xff09;和 Node.js&#xff08;一…

关于ORA-30625: method dispatch on NULL SELF argument is disallowed

文章目录 1. 创建type2. 创建测试表3. 创建type body4. insert 测试数据5. 引用type并执行6.原因7. 正确的方式 JOB 频繁出现ORA-30625&#xff1a;method dispatch on NULL SELF argument is disallowed。 这篇文章将对此错误进行重现并分析其原因 1. 创建type CREATE OR RE…

游戏行业洞察:分布式开源爬虫项目在数据采集与分析中的应用案例介绍

前言 我在领导一个为游戏行业巨头提供数据采集服务的项目中&#xff0c;我们面临着实时数据需求和大规模数据处理的挑战。我们构建了一个基于开源分布式爬虫技术的自动化平台&#xff0c;实现了高效、准确的数据采集。通过自然语言处理技术&#xff0c;我们确保了数据的质量和…

【PostgreSQL实现psql连接时候提示用户的密码有效时间】

如下内容使用session_exec插件结合自定函数实现。类似于触发器的原理。 功能需要严格在测试环境测试后&#xff0c;才可在正式环境使用。没有相关要求&#xff0c;还是建议直接查询pg_roles/pg_authid/pg_user&#xff1b; 一、判断是否需要修改用户密码和有效期的检查SQL 首…

【Emgu CV教程】7.1、图像锐化之Laplacian(拉普拉斯)算子锐化

文章目录 一、介绍二、举例1.原始素材2.代码3.运行结果 一、介绍 前面几篇讲的是图像平滑&#xff0c;就是抑制或消除噪声&#xff0c;并使得图像亮度及颜色变化更平缓的操作。在图像处理领域&#xff0c;与平滑操作相对应的&#xff0c;叫图像锐化。 图像锐化就是增强图像的边…

python OpenCV:seamlessClone泊松融合

一、seamlessClone函数的用法 翻译 https://www.learnopencv.com/seamless-cloning-using-opencv-python-cpp/ def seamlessClone(src, dst, mask, p, flags, blendNone): # real signature unknown; restored from __doc__"""seamlessClone(src, dst, mask, …

【Hudi】Upsert原理

17张图带你彻底理解Hudi Upsert原理 1.开始提交&#xff1a;判断上次任务是否失败&#xff0c;如果失败会触发回滚操作。然后会根据当前时间生成一个事务开始的请求标识元数据。2.构造HoodieRecord Rdd对象&#xff1a;Hudi 会根据元数据信息构造HoodieRecord Rdd 对象&#xf…

2024年【起重机司机(限桥式起重机)】试题及解析及起重机司机(限桥式起重机)证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重机司机(限桥式起重机)试题及解析考前必练&#xff01;安全生产模拟考试一点通每个月更新起重机司机(限桥式起重机)证考试题目及答案&#xff01;多做几遍&#xff0c;其实通过起重机司机(限桥式起重机)理论考试很…

Django里写一个get请求

在Django中处理GET请求也是很直接的。默认情况下&#xff0c;Django的视图&#xff08;view&#xff09;会处理GET请求。要创建一个简单的GET请求处理器&#xff0c;你可以按照以下步骤&#xff1a; 首先&#xff0c;确保你已经创建了一个Django项目。 在你的应用中创建一个视…