C++基础语法:函数探幽(一)内联函数,默认参数,函数重载

news2024/11/24 17:24:05

前言
     

       "打牢基础,万事不愁" .C++的基础语法的学习."学以致用,边学边用",编程是实践性很强的技术,在运用中理解,总结.      

引入

       <C++ Prime Plus> 6th Edition(以下称"本书")第8章内容解读

内联函数

         1>本书P253--8.1节C++内联函数第二段:常规函数调用也使程序跳到另一个地址(函数的地址),并在函数结束时返回.下一段:对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存。(黑体字是原话)

        ----非内联函数和内联函数在程序被编译后产生的机器指令不一样.

        调用非内联函数时,留下一个函数地址,CPU处理到函数地址时会跳到函数定义的位置执行代码,然后跳转回来(return)继续执行以下代码.编译后的内联函数,把代码一并放到当前位置,节省了CPU跳转执行和跳转返回的时间.见本书P254图8.1.所以,内联函数占内存多,但运行快.

        2>内联函数和寄存器变量register一样,是一种请求.即使定义成内联函数,编译器未必满足,见本书P254说明.

        3>内联函数不能递归

        4>内联函数优于宏定义

        C语言中的宏定义,不主动识别(),所以容易写错.内联函数没有这个顾虑.

        内联函数的使用场景

                代码内容少,调用次数不多,不用递归时可考虑使用内联函数

引用

        本书P255最后一段: 但引用变量的主要用途是用作函数的形参。通过将引用变量用作参数,函数将使用原始数据,而不是其副本.(黑体字是原话)

        引用已写过几篇文章,核心就在黑体字中,使用原值.

        引用主要用于表示单个变量的指针(不支持数组),多用于类对象引用做形参.如果想修改原值,用对象引用作形参,如果仅访问,对象引用前加const修饰.----代码的"潜在规则"

        本书P274中间8.2.7"何时使用引用参数"有详细使用说明 

        关于右值引用:本书截图如下

        测试代码如下

/*已测试*/
/*变量赋值左值引用,常量赋值右值引用*/
#include<iostream>
using namespace std;

int main(void) {
	int a = 3;	
	int&& b = 3;					//常量赋值给右值引用
	int& ref_c = a;					//变量赋值给左值引用
	int d = b;						//右值引用赋值给变量
	int e = ref_c;					//左值引用赋值给变量,相当于指针

	cout << "a的值是:" << a << endl;
	cout << "b的值是:" << b << endl;
	cout << "c的值是:" << ref_c << endl;
}

         区别左值引用和右值引用:左值引用不能接收常量,如int &f=3;//错误.右值引用可以接收常量.

         在什么情况下使用右值引用,暂时放一放.

默认参数

        概念:在定义函数时,设置一个(或多个)默认值.调用函数时,可以不传已设置为默认的值.

        函数回顾:

        函数的使用包括函数原型(声明),函数定义,函数调用三部分.

        函数原型由返回值类型,函数名,形参列表组成.

        非默认函数的定义和使用:       

void fun(Parameter pa);       //函数原型,函数声明

/*伪代码,函数定义*/
void fun(Parameter pa){       //函数定义,抬头和函数原型一样
    statement;                //语句,分号结束
    no return;                //返回值类型为void,无需return
}

fun(pa);                      //函数调用,pa为Parameter类型值或者变量

         默认函数的定义和使用:

void fun(Parameter pa=p);       //函数原型加入默认参数

/*伪代码,函数定义*/
void fun(Parameter pa=p){       //函数定义加入默认参数
    statement;                  
    no return;                  
}

fun();                          //函数调用之一,有了默认参数可以不传参
fun(pp);                        //函数调用之二,pp为Parameter类型值或者变量

         二者区别:在形参定义的时候传入默认参数,一套完整的数据表达:类型 变量=值;

         这个例子中表示为:Parameter pa=p; 分别对应了类型,形参变量和值;

         注意:形参列表中既有默认参数,又有非默认参数,默认参数应该顶右边写         

void fun(Parameter pa=p,Para p1);       //错误,默认参数应放右边
void fun(Para p1,Parameter pa=p);       //正确
         默认参数的使用场景

        当调用一个函数,经常给形参传入同一个值时,用默认参数将这个值设置为默认参数.

         如何使用默认参数

         和前面讲过的函数模板一样,(自用)关于程序的一些概念4:C++泛型初探-CSDN博客,使用默认参数属于"锦上添花"的操作.

        说明:函数模板是用来整合代码用的,能把相同逻辑的函数整合起来做成函数模板就做,整合不了就用本来的函数.函数的默认参数不用刻意去定义,当调用函数时发现经常传同一个参数时,修改原函数定义,使这个参数成为默认参数即可.

函数重载

        本书:函数多态是C++在C语言的基础上新增的功能。默认参数让您能够使用不同数目的参数调用同一个函数,而函数多态(函数重载)让您能够使用多个同名的函数。术语“多态”指的是有多种形式,因此函数多态允许函数可以有多种形式。类似地,术语“函数重载”指的是可以有多个同名的函数,因此对名称进行了重载。这两个术语指的是同一回事,但我们通常使用函数重载。可以通过函数重载来设计一系列函数——它们完成相同的工作,但使用不同的参数列表。

        函数重载的关键是函数的参数列表——也称为函数特征标 (function signature)。如果两个函数的参数数目和类型相同,同时参数的排列顺序也相同,则它们的特征标相同而变量名是无关紧要的。 C++允许定义名称相同的函数,条件是它们的特征标不同。如果参数数目和/或参数类型不同,则特征标也不同。 (黑体字是原话)

        ----解读:红色部分表示函数重载的概念:特征标不同的同名函数.

        "特征标相同"的概念:参数数目,类型,排列顺序相同,表示特征标相同.当三个特征中有任意一个不相同表示"特征标不相同",加上函数用相同名字(返回值类型无关),构成函数重载.注意:返回值类型不是特征标的一部分

         而变量名是无关紧要的

        解读:变量名对编译器来说是无关紧要的,前面提到过CPU不认识变量名(这里的变量名指函数名),只识别函数地址.但变量名对于编写函数和使用函数的人来说是很重要的,通过函数命名来明确要表达的逻辑.

        函数指针和函数重载的对比

        函数指针

/*伪代码*/
typedef ResultType (*p)(ParameterType1 pt1,ParameterType2 pt2); //声明函数指针

ResultType some_fun(ParameterType1 pt1,ParameterType2 pt2;      //p指向的函数

         上述代码声明了函数指针p指向这一类函数:返回值类型Resulttype,有类型ParameterType1和类型ParameterType2的形参.

/*伪代码*/
/*定义使用函数指针类型p做参数的函数fun*/
ResultType fun(p pfun),ParameterType1 pt1,ParameterType2 pt2){        
        return (*pfun)(pt1,pt2);                                //调用指针指向的函数
};    

===================================
/*调用fun*/
fun(some_fun,pt1,pt2);            

         函数重载

/*伪代码*/
ResultType1 fun_ol(ParameterType1 pt1,ParameterType2 pt2);      //重载形式1
ResultType2 fun_ol(ParameterType2 pt2,ParameterType1 pt1);      //重载形式2
ResultType1 fun_ol(ParameterType2 pt2);                         //重载形式3
ResultType2 fun_ol(ParameterType1 pt1);                         //重载形式4
ResultType1 fun_ol(ParameterType1* pt1);                        //重载形式5

        对于编译器来说,他们仍然是不同的函数,因为虽然变量名相同,但是函数地址不相同,编译后的程序找对应的函数进行调用; 但是对于程序员来说,他们是相同的函数,因为程序员只关心要实现的逻辑,不同的是实现的条件有所不同(传入的参数不同)

        对比函数指针和函数重载,函数指针是目的不同(函数名称不同),条件相同(特征标相同);函数重载是目的相同(函数名相同),条件不同(特征标不同).返回值类型函数指针必须相同,函数重载无要求.

        函数重载匹配 

        匹配原则1:和参数最接近的数据类型优先匹配.

        前面说过给函数传入参数必须满足的条件是:形参=传入的值       等式需成立 .那么问题来了,当传入的值同时可以传入两个函数时,该调用哪一个函数?举例:

void fun(int i);    //形参整型;
void fun(double d); //形参浮点型;

        fun(5)匹配void fun(int i);当没有定义void fun(int i)时,匹配void fun(double d)这是自动类型向上转换(整型值5自动转为浮点值5.0)而得来的.fun(5.0)匹配void fun(double d),不管有没有定义void fun(int i)都不会匹配到他,因为数据类型不会自动向下转换.

        匹配原则2:const值只能传给const形参.这是const修饰数据的特点,不懂的可以先复习.这并不是"匹配哪一个函数"的问题,而是是否能这样用的问题.举例:

void fun(const string& str);    //const引用作参数,记作函数A
void fun(string& str);          //引用作参数,记作函数B
============================
const string s="good";          //const值声明 
string s1="very good";          //非const值声明
============================
fun(s);                         //调用A,当未定义A时,不会调用B
fun(s1);                        //调用B,当未定义B时,调用A

       匹配原则3:和引用有关的匹配  ,截图自己看

        书上的例子:

函数重载互斥

        函数重载并不是随便定义的,当编译器不认识的时候不能定义函数重载,必须采用其他函数名定义函数.有几种情况会发生互斥:

        1.类型变量和类型引用互斥,不能形成函数重载

        

        您可能认为可以在此处使用函数重载,因为它们的特征标看起来不同。然而,请从编译器的角度来考虑这个问题。假设有下面这样的代码: 

        

        参数x与double x原型和double &x原型都匹配,因此编译器无法确定究竟应使用哪个原型。为避免这种混乱,编译器在检查函数特征标时,将把类型引用和类型本身视为同一个特征标(黑体字为本书原话)

        本着代码要多写的精神,果然发现问题.

        测试代码

#include<iostream>
using namespace std;

double cude(double d);		//二者只能出现一个
double cude(double& d);		//二者只能出现一个

int main(void) {
	double a = cude(5.0);	//没问题,可以调用非引用形参,5.0不能传给引用形参
	double& b = a;
	cout << a << endl;
	double c_val = cude(b);	//报错,有多个重载函数"cude"实例与参数列表匹配
}


double cude(double d) {
	return d;
}

double cude(double& d) {
	return d + 1;
};

         2.只有返回值类型不同,函数名和特征标相同的函数,不能形成函数重载.如前所述,返回值类型不是特征标的一部分.

测试代码

#include<iostream>
using namespace std;

int fun(int a);		//只能二选一定义
long fun(int a);	//错误定义,无法重载仅按返回类型区分的函数

         C++不允许以这种方式重载gronk( )。返回类型可以不同,但特征标也必须不同:         

         3.当函数重载和默认参数在一起使用时,默认参数不被看作特征标,有可能不形成函数重载. 

测试代码

#include<iostream>
using namespace std;

void print(int a, const string& str = "good");	//二选一
void print(int a);								//二选一

int main(void) {
	print(3);									//报错,有多个重载函数"print"实例与参数列表匹配
	print(3, "good");
}

void print(int a, const string& str = "good") {
	cout << "数字是:"<<a << "字符串是:" << str << endl;
	cout << "数字是:"<<a << "字符串是:" << str << endl;
}

void print(int a) {
	cout << "数字是:" << a << endl;
}

         函数重载互斥的解决办法:改变函数名称

如何使用函数重载 

        和前面的默认参数,函数模板一样,函数重载属于"锦上添花"的操作.

        实际应用中,类构造函数需要主动考虑函数重载. 

        此外只要使用了不同参数,就定义成不同名的函数,在优化代码的时候再考虑函数重载.

       

===================================内容分割线=============================  

题外话:函数重载是学习C++中又一个令人"烦躁"的地方.

            本来函数重载就是为了减少函数的数目,结果为了正确调用,要变得非常小心地编写程序.想减少工作量,反而增加了工作强度.学会了吧,实际用到的地方又不多.

===================================内容分割线============================   

后记 

        本书第8章后面还有函数模板的内容,这部分内容不少,特点和这篇帖子里的默认参数,函数重载一样,多数是用于优化代码,做"锦上添花"的事

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

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

相关文章

数据库事务处理技术——故障恢复

1. 数据故障恢复的宏观思路 我们知道DBMS是利用内存&#xff08;主存&#xff09;和外存&#xff08;辅存&#xff09;这样的存储体系进行数据库的管理&#xff0c;其中内存也就是我们常说的缓存是易失的。而事务时DBMS对数据库进行控制的基本单元&#xff0c;宏观上是由程序设…

函数、预解析、参数、参数列表、抛出异常、捕获异常

函数 命名函数 匿名函数 构造函数 纯函数 预解析 关键字var和function开头的语句块提前进行处理 处理过程&#xff1a;当变量和函数的声明处在作用域比较靠后的位置的时候&#xff0c;变量和函数的声明会被提升到作用域的开头。 解释代码和执行代码 因为是在所有代码执行之…

【软件建模与设计】-07-静态建模

目录 1、类之间关系 1.1、关联 1.1.1、关联的多重性 1.1.2、三元关联 1.1.3、一元关联 1.1.4、关联类 2、组合与聚合层次 2.1、组合 2.2、聚合 3、泛化/特化层次 4、约束 5、静态建模和UML 5.1、问题域的静态建模 6、系统上下文的静态建模 7、使用UML构造型对类…

JAVA Spring学习Day1

Maven Maven配置&#xff1a; Maven是Java项目的构建工具&#xff0c;使用pom.xml配置文件管理项目依赖、插件和构建目标。Spring Boot项目搭建&#xff1a; Spring Boot是基于Spring框架的快速开发框架&#xff0c;通过约定大于配置的理念简化了Spring应用的搭建和开发。 …

C# 设计模式之原型模式

总目录 前言 在软件系统中&#xff0c;当创建一个类的实例的过程很昂贵或很复杂&#xff0c;并且我们需要创建多个这样类的实例时&#xff0c;如果我们用new操作符去创建这样的类实例&#xff0c;这未免会增加创建类的复杂度和耗费更多的内存空间&#xff0c;因为这样在内存中…

数据可视化工具,免费无限制制作报表

许多企业在报表制作上投入了大量资金&#xff0c;使用各种收费软件&#xff0c;往往只能满足基本需求&#xff0c;且操作复杂&#xff0c;让人感到无比头疼。不过最近我发现之前一直在做数据大屏的山海鲸可视化&#xff0c;现在新增了报表功能&#xff0c;不仅各种功能都可以免…

创新食堂管理:采购系统源码与供应链APP开发详解

今天&#xff0c;笔者将从食堂采购系统源码与供应链管理APP开发的角度&#xff0c;探讨如何利用技术创新提升食堂管理效率&#xff0c;并为企业带来更大的价值。 一、食堂采购系统的核心功能与优势 食堂采购系统是指用于管理食堂物资采购流程的软件系统&#xff0c;其核心功能…

《学会 SpringMVC 系列 · 剖析入参处理》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

【C++】巧用缺省参数与函数重载:提升编程效率的秘密武器

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间 本章将分享缺省参数与函数重载相关知识&#xff0c;为了更加深入学习C打下了坚实的基础。本章重点在于缺省参数与函数重载使用前提与注意事项 &#x1f308;个人主页&#xff1a;是店小二呀 &#x1…

[CTFHub]ret2text-入土为安的第十二天

checksec pwn ida fn F5 main 点(_int64)v4 v大小为0x70栈基指针0x8返回地址 secure()

软件测试--python基础

一、python基础 (1)第一个python (2)python解释器 (3)基础语法 ①字面量 什么是字面量 常用的值类型 字符串 ②注释 ③变量 什么是变量 变量的特征 变量的目的是存储运行过程的数据 存储的目的是为了&#xff1a;重复使用 ④数据类型 type()语句 变量有类型吗&#xff1f;…

如何选择高品质SD存储卡—高耐用度、防水、防动、抗冲击

SD卡&#xff08;Secure Digital Memory Card&#xff09;是一种广泛使用的存储器件&#xff0c;因其快速的数据传输速度、可热插拔的特性以及较大的存储容量&#xff0c;广泛应用于各种场景&#xff0c;例如在便携式设备如智能手机、平板电脑、运动相机等&#xff0c;用于存储…

68.游戏分析工具设计以及更改辅助中存在的界面问题

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;易道云信息技术研究院 上一个内容&#xff1a;67.利用FreeLibrary函数实现无痕注入的核心代码 分析工具主要做的是 游戏公共内容分析&…

【前端 · 面试 】TCP 总结(一)—— 概述

最近我在做前端面试题总结系列&#xff0c;感兴趣的朋友可以添加关注&#xff0c;欢迎指正、交流。 争取每个知识点能够多总结一些&#xff0c;至少要做到在面试时&#xff0c;针对每个知识点都可以侃起来&#xff0c;不至于哑火。 image 前言 我们常常会听到“ TCP 三次握手、…

注册中心--Eureka

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f525;个人专栏&#xff1a;Spring Cloud实战&#x1f4d5;格言&#xff1a;吾愚多不敏&#xff0c;而愿加学欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 1.项目问题 2.解决URL问题 2.1解决思路 2.2注册中心 2.3 CAP理…

LeetCode3. 无重复字符的最长子串(java实现)

今天分享的题目是LeetCode3. 无重复字符的最长子串&#xff0c;来看题目描述&#xff1a; 无重复的最长子串&#xff0c;题目有可能有些小伙伴没读太懂&#xff0c;其实就是找到不重复的最长子串&#xff0c;比如eg3&#xff0c;pwwk&#xff0c;那么w出现了两次就不符合要求。…

SpringBoot中的server.context-path

一、问题引入 书接上回&#xff0c;SpringBoot 在 idea中的 .idea和 .iml文件-CSDN博客&#xff0c;我在boot-test的测试项目中使用的 SpringBoot版本为 1.3.5.RELEASE&#xff0c;新项目 cps-task中使用的版本为 2.4.8&#xff0c;造成了连接异常&#xff0c;问题很好解决&…

一文看懂Java反射、注解、UML图和Lambda表达式

反射 定义: 反射是 java 开发语言的特征之一&#xff0c;它允许 java 程序对自身进行检查(自审)&#xff0c;并能直接操作程序内部属性&#xff0c;即就是将类中的各种成分映射成一个 java 对象&#xff0c;利用反射技术可以对一个类进行解剖&#xff0c;将各个组成部分映射成…

c++STL容器中vector的使用,模拟实现及迭代器使用注意事项和迭代器失效问题

目录 前言&#xff1a; 1.vector的介绍及使用 1.2 vector的使用 1.2 1 vector的定义 1.2 2 vector iterator&#xff08;迭代器&#xff09;的使用 1.2.3 vector 空间增长问题 1.2.4 vector 增删查改 1.2.5vector 迭代器失效问题。 2.vector模拟实现 2.1 std::vect…

RAG 革命:NVIDIA 工作站如何成为企业 AI 的秘密武器

在深圳的一家科技初创公司&#xff0c;首席技术官李梅正在向她的团队展示一个令人兴奋的新项目。“看这个&#xff0c;” 她指着屏幕上的实时演示说&#xff0c;“我们刚刚用公司的技术文档训练了一个 AI 助手&#xff0c;它现在可以回答任何关于我们产品的问题&#xff0c;而且…