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;
}

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

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

相关文章

Axi接口的DDR3:参数,时序,握手机制

参考 AXI总线的Burst Type以及地址计算 | WRAP到底是怎么一回事&#xff1f;_axi wrap-CSDN博客 还有官方手册&#xff0c;名字太长想起来再写。 Transaction/Burst/Transfer/Beat Transaction指一次传输事务&#xff0c;实际上包括了address phase, data phase与response ph…

记录:Unity脚本的编写4.0

目录 前言导入音乐编写脚本 前言 之前使用脚本对uniry中的模型进行了控制&#xff0c;诸如使用键盘控制对象模型的移动或者使用鼠标对对象模型进行角度的切换&#xff08;或者是类似的东西&#xff09;&#xff0c;而我们在游戏的过程中&#xff0c;总是伴随着一些好听的bgm&a…

读书笔记之《敏捷测试从零开始》(一)

大家好&#xff0c;我是rainbowzhou。 子曰&#xff1a;学而时习之&#xff0c;不亦说乎&#xff1f;今天我想和大家分享一本测试书籍——《敏捷测试从零开始》。以下为我的读书笔记&#xff1a; 精彩片段摘录&#xff1a; 焦虑往往来自于对比&#xff0c;当你在自己的圈子里面…

Django学习笔记——文件上传(界面还怪好看得嘞)

定义文件上传函数 #文件上页面 def uploadFileIndex(request):return render(request, "uploadFile.html")#文件上传接口 def uploadFile(request):if request.method POST and request.FILES[file]:uploaded_file request.FILES[file]fs FileSystemStorage()# 选…

路飞项目多方式登录、手机号短信验证注册接口

登录注册页面分析 用户板块需要写的接口 用户名密码登录&#xff08;多方式登录&#xff09;获取手机验证码接口手机号验证码登录注册接口验证手机号是否存在接口 验证手机号是否存在 视图类 from rest_framework.viewsets import ViewSet from rest_framework.decorator…

YOLOv8优化:独家创新(Partial_C_Detect)检测头结构创新,实现涨点 | 检测头新颖创新系列

💡💡💡本文独家改进:独家创新(Partial_C_Detect)检测头结构创新,适合科研创新度十足,强烈推荐 Partial_C_Detect | 亲测在多个数据集能够实现大幅涨点 💡💡💡Yolov8魔术师,独家首发创新(原创),适用于Yolov5、Yolov7、Yolov8等各个Yolo系列,专栏文章提…

mac下配置环境-node以及nvm

当前配置环境主要针对于mac下系统&#xff0c;需要提前安装brew包 如需要配置&#xff0c;可查阅&#xff1a;Brew包的基本安装&#xff08;手把手教学&#xff09;-CSDN博客 如果是window环境配置&#xff0c;分享一个不错的帖子&#xff1a;nvm的安装和使用&#xff08;详细&…

【NPM】vuex 数据持久化库 vuex-persistedstate

在 GitHub 上找到&#xff1a;vuex-persistedstate。 安装 npm install --save vuex-persistedstate使用 import { createStore } from "vuex"; import createPersistedState from "vuex-persistedstate";const store createStore({// ...plugins: [cr…

Unity 通过jar包形式接入讯飞星火SDK

最近工作上遇到了要接入gpt相关内容的需求&#xff0c;简单实现了一个安卓端接入讯飞星火的UnitySDK。 或者也可以接入WebSocket接口的。本文只讲安卓实现 我使用的Unity版本为2021.3.27f1c2 Android版本为4.2.2 1.下载SDK 登陆讯飞开放平台下载如图所示SDK 2.新建安卓工程…

目标检测应用场景—数据集【NO.15】叶片虫害检测

写在前面&#xff1a;数据集对应应用场景&#xff0c;不同的应用场景有不同的检测难点以及对应改进方法&#xff0c;本系列整理汇总领域内的数据集&#xff0c;方便大家下载数据集&#xff0c;若无法下载可关注后私信领取。关注免费领取整理好的数据集资料&#xff01;今天分享…

Java实现ORM第一个api-FindAll

经过几天的业余开发&#xff0c;今天终于到ORM对业务api本身的实现了&#xff0c;首先实现第一个查询的api 老的C#定义如下 因为Java的泛型不纯&#xff0c;所以无法用只带泛型的方式实现api&#xff0c;对查询类的api做了调整&#xff0c;第一个参数要求传入实体对象 首先…

nodejs+wasm+rust debug及性能分析

文章目录 背景v8引擎自带的profilelinux的perf采集wasm三方库性能分析编译debug版本wasmrust程序debug调试异常模型正常模型结论优化 参考 Node使用火焰图优化CPU爆涨 - 掘金 【Node.js丨主题周】理解perf 与火焰图-腾讯云开发者社区-腾讯云 Easy profiling for Node.js Applic…

【1419. 数青蛙】

目录 一、题目描述二、算法原理三、代码实现 一、题目描述 二、算法原理 三、代码实现 代码实现1&#xff1a;我自己写的比较挫&#xff0c;但是比较简单。 class Solution { public:int minNumberOfFrogs(string croakOfFrogs) {unordered_map<char,int> hash;hash[c]…

函数式编程-Stream流

函数式编程.md 视频学习地址&#xff1a;00.函数式编程的重要性_哔哩哔哩_bilibili 1. 概述 1.1 为什么学&#xff1f; 能够看懂公司里的代码大数量下处理集合效率高代码可读性高消灭嵌套地狱 //查询未成年作家的评分在70以上的书籍 由于洋流影响所以作家和书籍可能出现重复…

word修改公式默认字体并打出漂亮公式

文章目录 word公式简介传统方法1——mathtype传统方法2——word自带公式编辑器最简洁方法——更改word自带公式字体快捷方式效果展示 word公式简介 word自带的公式字体Cambria Math不可否认很丑&#xff0c;要打出latex格式的漂亮字体很困难。使用Markdown工具很多只能导出为不…

MSVCR100.dll丢失修复方法,MSVCR100.dll丢失的解决方法

今天我要和大家分享的是&#xff1a;msvcr100.dll丢失的6种解决方法。 首先&#xff0c;让我们来了解一下msvcr100.dll丢失的原因。msvcr100.dll是Microsoft Visual C 2010的一个组件&#xff0c;它包含了许多运行库文件&#xff0c;这些文件是许多应用程序所必需的。当msvcr1…

使用 DCGAN 生成动漫面孔-附训练数据集下载

介绍 在这个项目中,我们的目标是使用深度卷积生成对抗网络(DCGAN)算法生成高质量的动漫面孔。该项目使用的数据集包含 63,632 个动漫面孔,经过精心策划,以确保质量和吸引力。这个项目背后的动机是实现一个简单的梦想,即生成完美的 waifus,可爱的女性动漫面孔,捕捉动漫…

算法、语言混编、分布式锁与分布式ID、IO模型

一、算法初识 数据结构和算法是程序的基石。我们使用的所有数据类型就是一种数据结构&#xff08;数据的组织形式&#xff09;&#xff0c;写的程序逻辑就是算法。 算法是指用来操作数据、解决程序问题的一组方法。 对于同一个问题&#xff0c;使用不同的算法&#xff0c;也…

python socket网络编程(传输一个图片数据)

服务端 import base64 import socket import numpy as np import cv2 import datetime import os sssocket.socket(socket.AF_INET,socket.SOCK_STREAM) ss.bind(("192.168.1.65",5)) #服务器绑定ip ss.listen(5) #开始监听 tcp1,addss.accept() #阻塞进程&#…