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

C++学习笔记之三(函数指针、调用、动态内存、模板)

C++

  • 1、函数&指针
    • 1.1、指针函数
    • 1.2、函数指针
      • 1.2.1、函数指针作为函数的传入参数
      • 1.2.2、函数指针作为函数的返回值
  • 2、传递
    • 2.1、值传递
    • 2.2、址传递
    • 2.3、引用传递
  • 3、多态
    • 3.1、虚方法和抽象方法
  • 4、动态内存
  • 5、模板
    • 5.1、函数模板
    • 5.2、类模板
    • 5.3、内联函数

1、函数&指针

1.1、指针函数

指针函数是函数,其中指针是定语,函数是名词。意为该函数的返回值为指针

int *Function_1(void);int temp = 9;
int *p = &temp;int *Function_1(void)     //Function函数返回一个int *的指针
{cout<<"I am a function"<<endl;return p;   //注意函数的作用域问题,别返回局部指针 
}

1.2、函数指针

函数指针是一个指针,其中函数是定语,指针是名词。意为该指针指向函数,即指针内存放的函数的地址

int (*point)(float);           //int指的是函数的返回值类型,float指的是函数的形参类型

举个例子:

int t_function(float);                  //define a functonint t_function(float num)
{cout<<"the input parameter is: "<<num<<endl;return int(num);                   //forcing a floating type to an integer
}int main()
{int (*p)(float);                   //define a function pointorp = t_function;                 //函数名经过运算之后以地址的形式出现cout<<(*p)(9.9)<<endl;return 0;	
} 

1.2.1、函数指针作为函数的传入参数

函数指针的本质就是指针,所以函数指针作为传入参数的用法和指针作为传入参数的用法是相同。和一般的数据类型一样,作为形参时,都需要给出完整的定义。而在调用的时候,传入其地址即可。

int add(int,int);
int sub(int,int);
int calc(int (*p)(int,int),int,int);         //function pointor as the input parameterint add(int num1,int num2)
{return num1+num2;
}
int sub(int num1,int num2)
{return num1-num2;
}
int calc(int (*p)(int,int),int num1,int num2)
{return (*p)(num1, num2);
}int main()
{int (*p)(int,int);             //define a function pointorcout<<calc(add,5,3)<<endl;     //input the function's address and the num1,num2.cout<<calc(sub,5,3)<<endl;return 0;	
} 

1.2.2、函数指针作为函数的返回值

函数指针的声明可以这样写:

int (*function(float))(int,int);

这里充分利用了C语言的运算优先级来分割语句,即括号的优先级大于其他运算符。所以以上的语句可以分为两个部分看,第一个部分 f u n c t i o n ( f l o a t ) function(float) function(float),第二个部分 i n t ( ∗ p ) ( i n t , i n t ) int (*p)(int,int) int(p)(int,int)。其中 p p p就是第一部分的 f u n c t i o n ( f l o a t ) function(float) function(float),相当于数学上的换元。
从第一部 f u n c t i o n ( f l o a t ) function(float) function(float)分可以看出,该语句所声明的是一个函数,且该函数有一个 f l o a t float float的传入参数。
从第二部分 i n t ( ∗ p ) ( i n t , i n t ) int (*p)(int,int) int(p)(int,int)可以看出,该函数的返回值是一个函数指针,且该函数指针的形式是 i n t ( ∗ p ) ( i n t , i n t ) int (*p)(int,int) int(p)(int,int),即该函数指针指向的是一个返回值为 i n t int int类型且两个传入参数都为 i n t int int类型的函数。
最后,在定义该函数的时候当然得把所以形参都加上:

int (*function(float num1))(int num2, int num3)
{cout<<"hello, i am a function and my return value is a function poineter"<<endl;//返回一个函数指针所指向的函数的地址return "a function's address to which the pointer points";   
}

2、传递

2.1、值传递

值传递是最简单的一种形式,但囿于太不灵活了。众所周知,每一个函数都有自己的栈区,而值传递就是栈区相互联系的其中一个桥梁。

void Square(int);void Square(int temp)
{int sum = temp*temp;cout<<"the square of this number is:"<<sum<<endl;
}int main()
{int num;cout<<"please enter the number:";cin>>num;Square(num);return 0;
}

该函数的功能是计算传入参数的平方,但无论在 S q u a r e Square Square里面怎么修改 t e m p temp temp,原来的数值 n u m num num还是没有任何改变。究其原因是传值的时候, S q u a r e Square Square会把 n u m num num的值复制下来,放在自己的栈区。

2.2、址传递

void Modify(int*);void Modify(int *temp)
{*temp = 9; 
}int main()
{int num;cout<<"please enter the number:";cin>>num;Modify(&num);cout<<"The revised number is:"<<num<<endl;return 0;
}

该函数的功能是把原来的值修改为9,传地址有两个好处,其中一个就是如上的代码所示,可以通过解引用地址来修改原来的值。另一个好处就是,可以传输数组、结构体、类等等东西,因为归根结底,拿到首地址都是可以访问这些结构的。
但传地址的本质还是 M o d i f y Modify Modify把拿到的地址复制一份到自己的栈区,然后再通过解引用自己栈区中的这个地址,去访问或修改原始数据。

2.3、引用传递

通俗的讲,引用传递是给一个变量取一个别名,且通过该别名索引到的内存空间依旧是原来的内存空间。
但从本质上来看,变量名在计算机底层本身就是不存在的,只是给编译器看的而已,所以何来再取一个别名这样的操作。所以在我个人来看,引用还是和指针一样,传递的是一个地址,只不过形式不一样。且引用传递的时候,所调用函数不不复制它到栈区中。这样既节省了内存,也简化了形式。

void Modify(int&);void Modify(int &temp)
{temp = 9; 
}int main()
{int num;cout<<"please enter the number:";cin>>num;Modify(num);cout<<"The revised number is:"<<num<<endl;return 0;
}

3、多态

多态性是指用一个函数名字定义若干个函数实现不同的函数,达到在不同情况调用同一函数名执行不同操作的功能。而为了更好的利用多态性,虚方法和抽象方法应运而生。

3.1、虚方法和抽象方法

虚方法是指父类定义一个虚方法,子类可以决定是否要覆盖。如果子类覆盖了,在“父类的指针,子类的对象”这种情况下,以子类的覆盖为准(如 f u n c t i o n 2 function2 function2),否则以父类的函数为主(如 f u n c t i o n 1 function1 function1)。
抽象方法即在形式上和虚方法很像,在其末端加一个 0 0 0而已。但不同的是,抽象方法在父类中是只定义不声明,等到了子类再定义,那么当函数执行的时候,当然有且只有以子类的函数实现为主。

class Superclass
{
public:void function_1(void){cout<<"the father's function 1"<<endl;}virtual void function_2(void){cout<<"the father's function 2"<<endl;}virtual void function_3(void)=0;                };class Subclass:public Superclass
{
public:void function_1(void){cout<<"the son's function 1"<<endl;}void function_2(void){cout<<"the son's function 2"<<endl;}void function_3(void){cout<<"the son's function 3"<<endl;}};int main()
{Superclass *p = new Subclass;p->function_1();		//print "the father's function 1"p->function_2();		//print "the son's function 2"		p->function_3();		//print "the son's function 3"return 0;
}

4、动态内存

内存按申请阶段分为静态内存和动态内存两种。静态内存是指程序在编译阶段申请内存空间,一般大小固定且不能再修改。比如 i n t int int t e m p temp temp = = = 3 ; 3; 3;这种基本数据类型,都是在编译阶段就给分配好的内存。
动态内存则指在运行阶段才向堆申请内存空间,一般用 n e w new new m a l l o c malloc malloc申请的都属于动态内存。

int main()
{int count;cout<<"please assign a value to count:";cin>>count;int *p = new int[count];for(int i=0;i<count;i++){*(p+i) = i;cout<<*(p+i)<<endl;}delete []p;              //用这种方式可以释放掉动态数组return 0;
}

5、模板

5.1、函数模板

一般用 t e m p l a t e < t y p e n a m e template<typename template<typename T > T> T>或者 t e m p l a t e < c l a s s template<class template<class T > T> T>来声明一个函数模板。使用模板的好处在于 T T T可以按照需求变成整型 ( i n t ) (int) (int)、浮点型 ( f l o a t ) (float) (float)、地址 ( a d d r e s s ) (address) (address)等等。这避免了传不同的参数就要写不同的重载函数的麻烦。

template <typename T>   	
void t_function(T);	template <typename T>   	
void t_function(T temp)	
{cout<<"the size of temp is:"<<sizeof(temp)<<endl;
}int main()
{int a = 1;double b = 1.0;t_function(a);			//print:"the size of temp is 4"t_function(b);			//print:"the size of temp is 8"return 0;
}

但使用模板也有一个比较繁琐的点就是每次使用前,都得声明一次。因为我们需要它来告诉编译器如何定义这个函数。

5.2、类模板

类模板其实和函数模板差不多了,只不过是在类的定义前加一个模板罢了。需要注意的部分我也打在注释上了。

template <typename T>   	
class Car
{
public:Car(T number){cout<<"the car license number is: "<<number<<endl;	}~Car(void){cout<<"over"<<endl;}void run(T);
};template <typename T>                          //you have to declare the template every times you use it.
void Car<T>::run(T kilometer)               //Car后面还要加个<T>,代表这个类方法定义的时候用了函数模板
{cout<<"the car ran "<<kilometer<<"km"<<'\n';
}int main()
{class Car <int>mycar(888);                 //在实例化的时候必须定义类模板是什么类型mycar.run(39);return 0;
}

当然,我们也可以同时使用多个函数模板,像这样。

template <typename T,typename U>   	
class Car
{
public:Car(T number){cout<<"the car license number is: "<<number<<endl;	}~Car(void){cout<<"over"<<endl;}void run(U);
};template <typename T,typename U>        //即便只要到了模板U,还是得吧T,U都带上
void Car<T,U>::run(U kilometer)
{cout<<"the car ran "<<kilometer<<"km"<<'\n';
}int main()
{class Car <int,float>mycar(888);                  //实例化的时候为定义了两种模板mycar.run(39.5);return 0;
}

5.3、内联函数

内联函数是 C + + C++ C++为了提高程序运行速度所做出的一项改进,在函数声明和定义之前加一个 i n l i n e inline inline关键字即可以使函数变成内联函数。
程序运行过程中调用常规函数,跳到对应的栈区执行该函数,执行之后再跳回来,而这会减缓程序运行的速度。内联函数的出现就是为了解决这一问题,内联函数是程序在编译过程中,将函数代码写入到调用它的函数的栈区。从而避免从栈区跳来跳去。譬如 A f u n c t i o n Afunction Afunction中调用到了 B f u n c t i o n Bfunction Bfunction,如果 B f u n c t i o n Bfunction Bfunction是一个常规函数,那么需要从 A f u n c t i o n Afunction Afunction的栈区跳到 B f u n c t i o n Bfunction Bfunction的栈区去执行 B u n c t i o n Bunction Bunction函数再跳回来。但如果 B f u n c t i o n Bfunction Bfunction是一个内联函数,则可以在编译的时候直接把 B f u n c t i o n Bfunction Bfunction的代码写入 A f u n c t i o n Afunction Afunction的栈区,那么当 A f u n c t i o n Afunction Afunction调用到 B f u n c t i o n Bfunction Bfunction的时候,就不需要在栈区之间跳来跳去了。
但这样做有一个缺点就是,如果在这段代码中有 A f u n c t i o n Afunction Afunction, C f u n c t i o n Cfunction Cfunction, D f u n c t i o n Dfunction Dfunction都调用到了 B f u n c t i o n Bfunction Bfunction,那这就会占用更多的内存。所以是否使用内联函数终究是效率和资源之间的平衡。

#include<iostream>
using namespace std;inline void Run(void);inline void Run(void)
{cout<<"running!"<<endl;
}int main()
{Run();return 0;
}

相关文章:

C++学习笔记之三(函数指针、调用、动态内存、模板)

C 1、函数&指针1.1、指针函数1.2、函数指针1.2.1、函数指针作为函数的传入参数1.2.2、函数指针作为函数的返回值 2、传递2.1、值传递2.2、址传递2.3、引用传递 3、多态3.1、虚方法和抽象方法 4、动态内存5、模板5.1、函数模板5.2、类模板5.3、内联函数 1、函数&指针 1…...

【LeetCode】57. 插入区间

1 问题 给你一个 无重叠的 &#xff0c;按照区间起始端点排序的区间列表。 在列表中插入一个新的区间&#xff0c;你需要确保列表中的区间仍然有序且不重叠&#xff08;如果有必要的话&#xff0c;可以合并区间&#xff09;。 示例 1&#xff1a; 输入&#xff1a;interval…...

实时消息传送:WebSocket实现系统后台消息实时通知

实时消息传送&#xff1a;WebSocket实现系统后台消息实时通知 WebSocket简介基本实现步骤后台服务器后端接口SimpMessagingTemplate MessageDto前端客户端 示例应用 在现代Web应用中&#xff0c;提供实时通知对于改善用户体验至关重要。WebSocket技术允许建立双向通信通道&…...

【MATLAB第79期】基于MATLAB的数据抽样合集(sobol、LHS拉丁超立方抽样、Halton、正交/均匀设计、随机rand函数)

【MATLAB第79期】基于MATLAB的数据抽样合集&#xff08;sobol、LHS拉丁超立方抽样、Halton、正交/均匀设计、随机rand函数&#xff09; 一、传统函数 1.指定区间随机生成数据&#xff08;小数&#xff09; [a b]区间随机数生成: Aa(b-a)rand(m,n) m&#xff1a;待生成矩阵A…...

matlab bin格式转txt输出

matlab bin格式转txt输出 clc,clear; fid fopen(\text.bin,rb); data fread(fid,Inf,int16); fclose(fid);fidfopen(\text.txt,w); fprintf(fid,%d\n,data); fclose(fid);...

Linux之I2C应用编程

I2C-Tools的交叉编译 tar xvf i2c-tools-4.2.tar.xz 首先解压下压缩包 cd i2c-tools-4.2 进入 i2c-tools-4.2目录 make USE_STATIC_LIB1 执行 make 将i2cset ,i2cget ,i2cdump,i2cdetect,i2ctransfer放到板子上 命令直接操作IIC设备 命令行直接操作iic向AP3216C传感器获取数据…...

java 实现定时任务

1、EnableScheduling spring自带的定时任务功能&#xff0c;使用比较简单方便&#xff1a; 1、需要定时执行的方法上加上Scheduled注解&#xff0c;这个注解中可以指定定时执行的规则&#xff0c;稍后详细介绍。 2、Spring容器中使用EnableScheduling开启定时任务的执行&…...

vue3实现在element Dialog 对话框中预览pdf文件

最近有一个需求就是点击按钮在弹框中去预览pdf文件&#xff0c;于是发现了一个HTML中比较重要的标签&#xff1a;embed&#xff0c;前面说的需求就可以用这个标签来实现&#xff0c;一起来学习一下吧。 embed标签是HTML中的一个非常重要的标签&#xff0c;它可以在你的网页上插…...

JVM第十七讲:调试排错 - Java 问题排查之Linux命令

调试排错 - Java 问题排查之Linux命令 本文是JVM第十七讲&#xff0c; Java 问题调试排错。Java 在线问题排查主要分两篇&#xff1a;本文是第一篇&#xff0c;通过linux常用命令排查。 文章目录 调试排错 - Java 问题排查之Linux命令在项目中&#xff0c;日志操作的常用命令1、…...

米哈游、复旦发布,具备感知、大脑、行动的大语言模型“智能体”

ChatGPT等大语言模型展示了前所未有的创造能力&#xff0c;但距AGI&#xff08;通用人工智能&#xff09;还有很大的距离&#xff0c;缺少自主决策、记忆存储、规划等拟人化能力。 为了探索大语言模型向AGI演变&#xff0c;进化成超越人类的超级人工智能&#xff0c;米哈游与复…...

企业知识库管理系统怎么做?

21世纪&#xff0c;一个全新的信息化时代&#xff0c;从最初的传统办公到现在的信息化办公&#xff0c;一个世纪的跨越造就了各种大数据的诞生。 知识库系统 在这个数据横行的时代&#xff0c;文档管理产品市场逐渐兴盛起来&#xff0c;企业知识库管理系统作为企业的智慧信息的…...

嵌入式养成计划-45----QT--事件机制--定时器事件--键盘事件和鼠标事件--绘制事件

一百一十五、事件机制 当这件事情发生时&#xff0c;会自动走对应的函数处理&#xff08;重写的事件函数&#xff09; 115.1 事件处理简介 什么是事件&#xff1f; (重点) 件是由窗口系统或者自身产生的&#xff0c;用以响应所发生的各类事情&#xff0c;比如用户按下并释放…...

git远程仓库、开发者使用流程、ssh连接

git远程仓库 https://www.cnblogs.com/liuqingzheng/p/15328319.html 远程仓库有&#xff1a; : github gitlab gitee 在gitee上创建了仓库 &#xff08;确保仓库是空的&#xff09;本地&#xff1a;git init本地&#xff1a;git commit -m 提交版本指定远程仓库地址 添加一…...

SpringBoot (3) Profiles,外部化配置,自定义starter

目录 1 Profiles 1.1 "组件"环境隔离 1.1.1 标识环境 1.1.2 激活环境 1.2 "配置"环境隔离 1.2.1 添加"副配置文件" 1.2.2 激活环境 2 外部化配置 2.1 配置优先级 2.2 快速部署 3 自定义starter 3.1 基本抽取 3.1.1 导yaml提示包 3…...

【C++】类型转换(dynamic_cast,const_cast,static_cast,reinterpret_cast)

&#x1f30f;博客主页&#xff1a; 主页 &#x1f516;系列专栏&#xff1a; C ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ &#x1f60d;期待与大家一起进步&#xff01; 文章目录 C语言中的类型转换一、static_cast二、reinterpret_cast三、 const_cast四、 dynamic…...

冷笑话-1

代码检视时&#xff0c;程序员A看着下面的代码&#xff0c;疑惑地问程序员B&#xff1a;“为什么不用重载&#xff1f;” class MyClass {public MyClass queryById(long id) { //......}public MyClass queryByName(String Name) { //......}public MyClass queryByIdAndNam…...

模拟退火算法(SA)求解旅行商问题(TSP)python

目录 一、模拟退火算法求解TSP&#xff08;city14&#xff09;的python代码 二、city14的运行结果 三、 模拟退火算法求解TSP&#xff08;city30&#xff09;的python代码 四、city30的运行结果 一、模拟退火算法求解TSP&#xff08;city14&#xff09;的python代码 impor…...

Intelijj使用Gitee团队开发

初始化项目到Gitee服务器 成功标识&#xff1a; 添加团队成员 点击管理——仓库成员设置——开发者 2.添加仓库成员 &#xff08;最多不超过5人&#xff09; 3.通过链接或者二维码邀请新成员&#xff0c;或者可以自己手动添加新成员并提交 多人项目仓库创建完成 通…...

气象台使用vr模拟仿真实训教学降低成本投入

气候仿真实验室用于模拟高低温、高湿、干燥、阳光光照、降雨、降雪、覆冰、雾天与强风等多种环境适应性试验等气候和环境条件&#xff0c;在环境试验中&#xff0c;温度、湿度、光照、降雨这些常见的仿真环境都很容易实现。而比较少见的雾天、强风、降雪等环境就比较难。因此为…...

智能井盖是什么?万宾科技智能井盖传感器有什么特点

智能井盖是一种基于物联网和人工智能技术的新型城市设施。它不仅具备传统井盖的功能&#xff0c;还能通过数字化、自动化的方式实现远程监控和智能管理&#xff0c;提升城市运行效率和服务水平。 WITBEE万宾智能井盖传感器EN100-C2是一款井盖异动监测的传感终端。对窨井盖状态(…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...