一、引言

        上期我们介绍了C++11的大部分特性。C++11的初始化列表、auto关键字、右值引用、万能引用、STL容器的的emplace函数。

        要补充的是右值引用是不能取地址的,我们程序员一定要遵守相关的语法。操作是未定义的很危险。

二、        仿函数和函数指针

        我们先从仿函数的形成和函数指针的形成开始介绍起来,有了这个大家会更好的清楚lamda表达式、装配器function和绑定器bind。

        仿函数顾名思义就是类似于函数,肯定不是函数,但是肯定具有和函数一样的功能。那问题来了为什么不用函数指针呢?

        函数指针使用起来还是有点问题的。首先需要typedef一个固定的函数类型。没错这个函数是固定的不可变的,参数类型是固定的。那为什么我们不用上一章讲过的C语言不定参数。C语言的不定参数也需要传入类型,这个点是比较麻烦的。而且明确的就是类型不是字符串,因为不定参数是宏定义而不是真正的函数。

        为了方便比较我们也来使用一下函数指针吧。

#include <stdio.h>// 函数指针的模拟,随便写的类型名。
// 打印数组。// 定义函数指针
typedef void (*ptest)(int* a, int n);void test(int* a, int n)
{for (int i = 0;i < n;++i){printf("%d   ", a[i]);}printf("\n");
}void function(ptest func,int* a,int n)
{func(a,n);
}int main()
{int a[] = { 1,2.3,4,5,6,7,8 };int n = sizeof(a) / sizeof(a[0]);function(test,a,n);return 0;
}

        那我们是如何实现仿函数的呢?C++用的最多的就是类和对象,利用模板的特性我们可以传入各种不同的类,那仿函数的原型毫无疑问就是类,类中包含的函数传入到另一个函数或是其他类中也可以调用内部中的函数。这样我们实现一个基本的多态。多种形态。和特殊化处理。那我们该如何命名类中的函数。既然叫函数就应该和函数差不多,不然可读性不是很好用的也麻烦,而且本来就是当函数来使用没必要取什么特殊的名字。这样我们就实现出了一个类函数。

class 类名
{public:// 运算符重载 : operator + 符号 // 就可以通过符号与类结合调用。// 这完全就是C++基于类和变量之间不同的考量。返回类型 operator () (参数){; // …………所要调用的方法和所要实现的内容。}
};     //这个分号千万不要忘记了,就把它当做是C语言定义变量。

        那下面我们来做一个简单的练习。首先我们要知道C++中包含排序方法。虽然排序的方法多种多样,但是排序标准都各有不同。并不一定是基于简单的比较大小,有可能是运用了特殊的比较方法。还可能会有顺序的差异例如升序、降序。所以我们使用C++库<algorithm>中std::sort()函数进行排序。另外排序标准由我们来进行确定。

#include <iostream>
#include <algorithm>// 仿函数。
class Func
{
public:// 这里依据函数实现的不同而返回类型不同。// 还可以加入模板。template<typename T>bool operator () (T i, T j){// 升序的话是后面的数据大于前面的数据。// 我们为了演示反其道而行之。return i > j;}
};int main()
{int a[] = { 1 , 2 , 4 , 5 , 6 , 7 , 8 };int n = sizeof(a) / sizeof(a[0]);// 起始位置和末尾位置的下一个(有效位置的下一个,也就是无效位置。更确切的讲是规定范围。)// 最后一个参数是用来传递排序标准的,什么都不传的话默认是升序(缺省参数)。// 如果是STL容器的话直接用里面的begin函数和end函数。std::sort(a, a + n);// 范围forfor (int x : a){std::cout << x << "  ";}std::cout << std::endl;std::sort(a, a + n, Func());for (int x : a){std::cout << x << "  ";}std::cout << std::endl;return 0;
}

三、        Lamda表达式

        上述的仿函数虽然与函数指针简便不少,例如不用考虑参数的类型,不用定义函数指针。但C++标准委员会还是觉得不好用,能不能出现一个匿名的函数呢?直接不用写名字。这个其实最开始是python最先搞出来的。C++看到后觉得好用直接也实现一个出来了。底层就是我们上面实现的仿函数,只是用完就丢这个特性和仿函数不一样。

[捕捉列表:值捕捉&+变量、对象=引用。值引用:变量、对象=const引用]
// 如果只有一个&,说明是引用函数中的所有变量、对象。
->返回类型(没有就省略这个->)(参数类型)
{;     //…………实现的方法。
}

        接下来我们还是使用std::sort()函数进行练习。

        只需要更改其中的一行代码就行了。lamda表达式其实就是匿名函数(没有名字,说明接下来无法通过名字来调用,同时也意味着无法递归。(函数自己本身调用一个新的原本函数自己本身,可以理解创建新的自己分身。)

std::sort(a, a + n,[](int i,int j)
{return i > j;
});

四、        包装器function、绑定器bind介绍

       这两个函数都是库<functional>文件中的类。

        std::function和函数指针有着异曲同工之妙,在项目中对于函数的发封装起到非常大的作用。std::function可以与lamda表达式、仿函数和bind装配器有着不错的兼容。函数指针并不能做到这点。此外函数指针为了重复使用一般都是泛型编程(返回值或是参数都是用void*,这样就可以不用管参数的类型,参数的数量,返回值的数量和类型,但是这样出错了非常不好检查出来。)

        std::bind包装器,常常用于为了一个类能够调用其他类中的类函数,或者是固定好函数的参数,实现编程的隐蔽性和泛型编程。

        下面我们以打印数组来做实例对象。

#include <iostream>
#include <algorithm>
#include <functional>class Func
{
public:void print(int* a,int n){for (int i = 0; i < n; ++i){std::cout << a[i] << "  ";}std::cout << std::endl;}
};int main()
{int a[] = { 1 , 2 , 4 , 5 , 6 , 7 , 8 };int n = sizeof(a) / sizeof(a[0]);// 我们尝试使用std::function包装仿函数。// 还可以和模板进行搭配。// 调用未加static的类函数需要用到类中的指针或是引用。std::function<void(Func, int*, int)> func1 = &Func::print;func1(Func(), a, n);// 绑定器可以固定参数,调用更加方便,也防止别人乱改,也通过std::placeholders库中的_n来确定该输入几个参数。// 一般可以将std::bind看作function的初始化类型。// 使用std::bind赋值给std::function的时候,记得已经在bind固定了的参数的类型从function中去掉。// 因为参数已经固定无需再传参了。std::function<void(int*,int)> func2 = std::bind(&Func::print,Func(), std::placeholders::_1,std::placeholders::_2);func2(a, n);std::function<void()> func3 = std::bind(&Func::print, Func(), a, n);// 当然可以直接将所有参数都固定,直接变为无参函数。func3();return 0;
}

五、        智能指针

        我们在系统申请空间,总是要还的,所以我们常常需要手动释放它。但是这非常不方便。所以C++搞出了智能指针,自动释放申请空间或是自动关闭某个开关。但是最初的智能指针被骂的非常惨。因为C++根本没有考虑多引用、多指向的情况。unique_ptr是明确了只有一个指针指向这块空间。

        shared_ptr是允许多个指针指向申请的空间,运用了引用计数的概念,初始化时申请了一块空间进行统计使用同一空间的指针数量。当引用为零时自动释放申请空间。都使用shared_ptr了,肯定赋值的对象、接受的参数肯定也是shared_ptr。

        weak_ptr虚指针,防止shared_ptr指向过多,不能释放,造成内存泄漏,从而导致服务器崩溃。在shared_ptr互相指向时使用,shared_ptr不增加引用计数。相互指向造成你不释放,我也不释放的难题。不能单独使用,只能和shared_ptr一起使用。

template<class T>class unique_ptr{public:explicit unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}// 像指针⼀样使⽤ T& operator*(){return *_ptr;}T* operator->(){return _ptr;}unique_ptr(const unique_ptr<T>& sp) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;unique_ptr(unique_ptr<T>&& sp):_ptr(sp._ptr){sp._ptr = nullptr;}unique_ptr<T>& operator=(unique_ptr<T>&& sp){delete _ptr;_ptr = sp._ptr;sp._ptr = nullptr;}private:T* _ptr;};template<class T>class shared_ptr{public:explicit shared_ptr(T* ptr = nullptr): _ptr(ptr), _pcount(new int(1)){}template<class D>
{return _ptr;}int use_count() const{return *_pcount;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;int* _pcount;//atomic<int>* _pcount; function<void(T*)> _del = [](T* ptr) {delete ptr; };};// 需要注意的是我们这⾥实现的shared_ptr和weak_ptr都是以最简洁的⽅式实现的, // 只能满⾜基本的功能,这⾥的weak_ptr lock等功能是⽆法实现的,想要实现就要 // 把shared_ptr和weak_ptr⼀起改了,把引⽤计数拿出来放到⼀个单独类型,shared_ptr // 和weak_ptr都要存储指向这个类的对象才能实现,有兴趣可以去翻翻源代码 template<class T>class weak_ptr{public:weak_ptr(){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.get()){}weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get();return *this;}
private:T* _ptr = nullptr;};
}

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

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

相关文章

性能优化三剑客:`memo`, `useCallback`, `useMemo` 详解

性能优化三剑客&#xff1a;memo, useCallback, useMemo 详解 作者&#xff1a;码力无边各位React性能调优师&#xff0c;欢迎来到《React奇妙之旅》的第十二站&#xff01;我是你们的伙伴码力无边。在之前的旅程中&#xff0c;我们已经掌握了如何构建功能丰富的组件&#xff0…

好用的电脑软件、工具推荐和记录

固态硬盘读写测试 AS SSD Benchmark https://gitee.com/qlexcel/common-resource-backup/blob/master/AS%20SSD%20Benchmark.exe 可以测试SSD的持续读写、4K随机读写等性能。也可以测试HDD的性能。 操作非常简单&#xff0c;点击Start(开始)即可测试。 体积小&#xff0c;免安…

Spring Task快速上手

一. 介绍Spring Task 是Spring框架提供的任务调度工具&#xff0c;可以按照约定的时间自动执行某个代码逻辑&#xff0c;无需依赖额外组件&#xff08;如 Quartz&#xff09;&#xff0c;配置简单、使用便捷&#xff0c;适合处理周期性执行的任务&#xff08;如定时备份数据、定…

函数(2)

6.定义函数的终极绝杀思路&#xff1a;三个问题&#xff1a;1.我定义函数&#xff0c;是为了干什么事情 函数体、2.我干完这件事&#xff0c;需要什么才能完成 形参3.我干完了&#xff0c;调用处是否需要继续使用 返回值类型需要继续使用 必须写不需要返回 void小程序#include …

BGP路由协议(一):基本概念

###BGP概述 BGP的版本&#xff1a; BGP-1 RFC1105BGP-2 RFC1163BGP-3 RFC1267BGP-4 RFC1771 1994年BGP-4 RFC4271 2006年 AS Autonomous System 自治系统&#xff1a;由一个单一的机构或者组织所管理的一系列IP网络及其设备所构成的集合 根据工作范围的不同&#xff0c;动态路…

mit6.031 2023spring 软件构造 笔记 Testing

当你编码时&#xff0c;目标是使程序正常工作。 但作为测试设计者&#xff0c;你希望让它失败。 这是一个微妙但重要的区别。 为什么软件测试很难&#xff1f; 做不到十分详尽&#xff1a;测试一个 32 位浮点乘法运算 。有 2^64 个测试用例&#xff01;随机或统计测试效果差&am…

【Unity开发】Unity核心学习(三)

四、三维模型导入相关设置 1、Model模型页签&#xff08;1&#xff09;场景相关&#xff08;2&#xff09;网格相关&#xff08;3&#xff09;几何体相关2、Rig操纵&#xff08;骨骼&#xff09;页签 &#xff08;1&#xff09;面板基础信息&#xff08;i&#xff09;None&…

C#语言入门详解(17)字段、属性、索引器、常量

C#语言入门详解&#xff08;17&#xff09;字段、属性、索引器、常量前言一、字段 Field二、属性三、索引器四、常量内容来自刘铁猛C#语言入门详解课程。 参考文档&#xff1a;CSharp language specification 5.0 中文版 前言 类的成员是静态成员 (static member) 或者实例成…

Total PDF Converter多功能 PDF 批量转换工具,无水印 + 高效处理指南

在办公场景中&#xff0c;PDF 格式的 “不可编辑性” 常成为效率瓶颈 —— 从提取文字到格式转换&#xff0c;从批量处理到文档加密&#xff0c;往往需要多款工具协同。Total PDF Converter 破解专业版作为一站式 PDF 解决方案&#xff0c;不仅支持 11 种主流格式转换&#xff…

[Windows] WPS官宣 64位正式版(12.1.0.22525)全新发布!

[Windows] WPS官宣 64位正式版 链接&#xff1a;https://pan.xunlei.com/s/VOYepABmXVfXukzlPdp8SKruA1?pwdeqku# 自2024年5月&#xff0c;WPS 64位版本在WPS社区发布第一个内测体验安装包以来&#xff0c;在近一年多的时间里&#xff0c;经过超过3万名WPS体验者参与版本测试…

WinExec

函数原型&#xff1a; __drv_preferredFunction("CreateProcess","Deprecated. See MSDN for details") WINBASEAPI UINT WINAPI WinExec(__in LPCSTR lpCmdLine,__in UINT uCmdShow); preferred : 更好的 __drv_preferredFunction("CreateProcess…

基于GA遗传优化的双向LSTM融合多头注意力(BiLSTM-MATT)时间序列预测算法matlab仿真

目录 1.前言 2.算法运行效果图预览 3.算法运行软件版本 4.部分核心程序 5.算法仿真参数 6.算法理论概述 7.参考文献 8.算法完整程序工程 1.前言 时间序列预测是机器学习领域的重要任务&#xff0c;广泛应用于气象预报、金融走势分析、工业设备故障预警等场景。传统时间…

Multi-Head RAG: Solving Multi-Aspect Problems with LLMs

以下是对论文《Multi-Head RAG: Solving Multi-Aspect Problems with LLMs》的全面解析&#xff0c;从核心问题、方法创新到实验验证进行系统性阐述&#xff1a;​​一、问题背景&#xff1a;传统RAG的局限性​​传统检索增强生成&#xff08;RAG&#xff09;在处理​​多维度复…

Jenkins 全方位指南:安装、配置、部署与实战应用(含图解)

一、Jenkins 安装 1.1 系统要求 基础环境&#xff1a;Java 8 或 Java 11&#xff08;推荐&#xff09;、至少 2GB 内存、10GB 以上磁盘空间 支持系统&#xff1a;Windows、Linux&#xff08;Ubuntu/CentOS&#xff09;、macOS 网络端口&#xff1a;默认使用 8080 端口&…

以国产IoTDB为代表的主流时序数据库架构与性能深度选型评测

> &#x1f4a1; 原创经验总结&#xff0c;禁止AI洗稿&#xff01;转载需授权 > 声明&#xff1a;本文所有观点均基于多个领域的真实项目落地经验总结&#xff0c;数据说话&#xff0c;拒绝空谈&#xff01; 目录 引言&#xff1a;时序数据库选型的“下半场” 一、维…

7.2elementplus的表单布局与模式

基础表单<template><el-form ref"ruleFormRef" :model"form" :rules"rules" label-width"100px"><el-form-item label"用户名" prop"username"><el-input v-model"form.username"…

PyTorch实战(3)——PyTorch vs. TensorFlow详解

PyTorch实战&#xff08;3&#xff09;——PyTorch vs. TensorFlow详解0. 前言1. 张量2. PyTorch 模块2.1 torch.nn2.2 torch.optim2.3 torch.utils.data3. 使用 PyTorch 训练神经网络小结系列链接0. 前言 PyTorch 是一个基于 Torch 库的 Python 机器学习库&#xff0c;广泛用…

在win服务器部署vue+springboot + Maven前端后端流程详解,含ip端口讲解

代码打包与基本配置 首先配置一台win系统服务器&#xff0c;开放你前端和后端运行的端口&#xff0c;如80和8080 前端打包 前端使用vue3&#xff0c;在打包前修改项目配置文件&#xff0c;我使用的是vite所以是vite.config.js。 import { defineConfig } from vite import …

Springcloud-----Nacos

一、Nacos的安装 Nacos是阿里推出的一种注册中心组件&#xff0c;并且已经开源&#xff0c;目前是国内最为流行的注册中心组件。下面我们来了解一下如何安装并启动Nacos。 Nacos是一个独立的项目&#xff0c;我们可以去GitHub上下载其压缩包来使用&#xff0c;地址如下&#x…

腾讯云重保流程详解:从预案到复盘的全周期安全防护

摘要 腾讯云针对国家级重大活动&#xff08;如进博会、冬奥会等&#xff09;提供的网络安全保障服务&#xff08;重保&#xff09;是一套系统化的主动防御体系。本文从“事前准备”“事中响应”“事后复盘”三个核心阶段出发&#xff0c;结合民生银行等典型用户的实战案例&…