C++基础——对于C语言缺点的补充(2)

news2024/11/26 2:46:10

       上篇文章中说到,为了解决C语言会出现人为定义的函数和库函数出现重定义的错误,C++引入了一个新的概念,即命名空间,通过认为定义命名空间,来解决上述问题。

       在本篇文章中,将继续介绍C++相对于C语言不足来进行的补充,例如:缺省参数,重载函数等等。

1. 缺省参数:

1.1 缺省参数的定义以及单个缺省参数简单应用:

       缺省参数的定义大致如下:缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。例如下面的函数:

void PRINT(int a = 0)
{
	cout << a << endl;
}

int main()
{
	PRINT(0);
	PRINT(10);
	return 0;
}

       不难发现,相比C语言中的函数书写方法,C++中,对于函数的形式参数给定了一个初始值0,该值就是上面定义中所说的缺省值。对于这种带有缺省值的函数,其调用结果如下:

       这也证实了定义中的一句话,如果没有对函数的实参进行指定赋值,则在函数执行时默认采用形参,反之采用实参。

1.2 多个缺省参数的应用:

       在上面的情况中,函数只有单一的缺省值。当函数的缺省值数量大于1时,函数的调用方式会更加多样,例如:

void PRINT(int a = 10, int b = 20, int c = 30)
{
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
}

int main()
{
	PRINT();
	PRINT(1);
	PRINT(1, 2);
	PRINT(1, 2, 3);
	return 0;
}

 运行结果为:

       通过上面的例子可以看到,针对这种缺省值数量大于1的情况下,函数的调用会因为实参的给定而给出不用的效果。

       但是对于实参数值的给定,需要注意,实参的给定必须是连续的,中间不能有跳跃的情况,例如:

PRINT(, 2, );

1.3 全缺省与半缺省:

      全缺省参数就是上一部分的代码所展示,即所有的形参都给定了缺省值。对于半缺省,则是对于形式参数,不全部给定缺省值,例如:

void PRINT(int a, int b = 20, int c = 30)

但是对于半缺省参数的给定需要注意以下两点:

1. 缺省值必须从右向左给定,且必须是连续的。

2. 当函数的定义和声明分离时,缺省参数不能同时在声明和定义中出现。一般是将缺省参数给定到函数声明中

2. 函数重载:

2.1 函数重载的定义及类型:

       函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。例如下面给出的例子:

int ADD(int num1, int num2)
{
	cout << "int ADD(int num1, int num2) = ";

	return num1 + num2;
}

double ADD(double num1, double num2)
{
	cout << " double ADD(double num1, double num2) = ";

	return num1 + num2;
}
int main()
{
	cout << ADD(1, 2) << endl;
	cout << ADD(1.1, 2.2) << endl;
}

       在上述代码中,建立两个函数,且这两个函数函数名相同,都是ADD,但是这两个函数的返回值类型,以及参数类型都不同,此时符合上方函数重载的定义,构成了函数重载。

      对于上述代码,运行结果如下:

         

       上面的例子展示了函数的类型不同,以及其参数类型不同的情况下可以构成重载。当函数的参数数量不同时,即:

void fun()
{
	cout << "fun()" << endl;
}

void fun(int a)
{
	cout << "fun(a)" << endl;
}
int main()
{
	fun();
	fun(1);
}

      此时符合重载定义中参数数量不同这一特性,程序依旧可以正常运行,运行结果如下:

    但是上述函数有一种特殊情况需要注意,即:将函数的参数给定一个缺省值,例如:

void fun()
{
	cout << "fun()" << endl;
}

void fun(int a = 0)
{
	cout << "fun(a)" << endl;
}
int main()
{
	fun();
	fun(1);
}

      运行程序,编译器会报错。这是因为在存在一个不含参数的函数与另一个有参数,且参数有缺省值的情况下,同时运行两个函数会造成二义性,即:编译器不能判断在main函数中,调用函数时,调用的函数是上面两个函数哪一个函数。

    对于函数重载,还有一种方式,即类型的顺序不同,具体情况如下方代码所示:

void fun(int a, char b)
{
	cout << "void fun(int a, char b)" << endl;
}

void fun(char a, int b)
{
	cout << "void fun(char a, int b)" << endl;
}
int main()
{
	fun(2, 1.1);
	fun(2.2, 1);
	return 0;
}

3. 引用:

3.1 引用的概念以及基本使用:

       在C++中,引用是基于C语言中指针的不足,为解决这些不足创建的。引用的概念如下:引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

      对应代码如下:

int main()
{
	int a = 1;
	int& c = a;
}

     main函数中,第一行代码创建了一个名为a的整型变量,第二行则是引用,即给已有的整型变量a取了一个别名叫做c

     在上面的概念中提到,编译器不会为引用变量开辟内存空间,引用与其引用的变量共用一块内存空间。因此,加入对引用进行更改,相应的变量也会更改,例如对于下面代码:

int main()
{
	int a = 1;
	int& c = a;
	cout << "更改前 a=" << a << endl;
	cout << "更改前 c=" << c << endl;
	c++;
	cout << "更改后 a=" << a << endl;
	cout << "更改后 c=" << c << endl;
	return 0;
}

运行结果为:

引用不光可以用在已有的变量上,也可以对引用进行引用,即:

int main()
{
	int a = 1;
	int& c = a;
	int& e = c;
	cout << "a = " << a << endl;
	cout << "c=" << c << endl;
	cout << "e=" << e << endl;
	return 0;
}

 运行结果为:

        对于引用而言,其最大的价值适用于做参数,例如在之前文章中提到到交换函数:

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
int main()
{
	int x = 5;
	int y = 1;
	Swap(&x, &y);

	printf("%d %d", x, y);
	return 0;
}

        由于函数形参只是实参的一份临时拷贝,形参的改变并不会影响实参,所以在传递参数时,需要传递变量的地址。

        如果利用引用的特性,即:引用的改变会引起变量的改变,来实现交换函数,则可以一定程度上化简上述函数,即:
 

void Swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
int main()
{
	int x = 5;
	int y = 1;
	Swap(x, y);

	printf("%d %d", x, y);
	return 0;
}

      运行结果如下:

3.1 使用引用的注意事项: 

3.1.1 引用与初始化:

       在指针这部分中,如果一个指针只是被创建但是不被初始化,则这个指针称为野指针。不过指针的初始化并不是必然的,但是对于引用来说,必须被初始化,例如:

int main()
{
	int a = 0;
	int& b;
	return 0;
}

由于引用没有被初始化,因此程序错误。

3.1.2 引用能不能被改变指向:

        在数据结构关于的链表的文章中,如果需要在链表中插入一个新的结点,需要改变链表中指针的指向,但是对于引用而言,不能被改变指向,例如:

int main()
{
	int a = 0;
	int& b = a;

	int c = 1;
	printf("%p %p %p", &a, &b, &c);
	printf("\n");
	b = c;
	cout << "a = " << a << "b = " << b << "c = " << c << endl;
	printf("%p %p %p", &a, &b, &c);
	return 0;
}

运行结果如下:


       通过结果不难发现,在执行了b=c这一条代码后,b的地址依旧与a的地址相同,说明没有改变其指向。只是改变了a,b的值。

      因此,引用虽然在一定程度上弥补了指针的不足,但是由于引用并不能改变指向,因此引用并不能完全替代指针。

3.2 引用返回:

3.2.1 引用返回的概念:

在解释本节标题的内容之前,首先需要了解一个概念:

int fun()
{
	int n = 0;
	n++;

	return n;
}
int main()
{
	int ret = fun();
	return 0;
}

       图中给定的程序的函数的返回值返回n,在函数外部创建了一个名为ret的变量来接收函数返回值,但是,由于在函数fun运行结束后,该函数对应的栈帧全部销毁,因此,ret并不是直接接收函数的返回值,而是返回值先被存到寄存器中,ret通过寄存器来间接接收这个值。

      此时,如果将函数返回的返回值改为下方代码所示,即:

int& fun()
{
	int n = 0;
	n++;

	return n;
}
int main()
{
	int ret = fun();
	return 0;
}

此时再去运行程序,编译器会给出警告。这是因为在这种返回类型下,返回值并不能确定,如果编译器在函数结束后直接清理掉栈帧,则返回空,如果编译器在函数结束后,并不会立即清理掉栈帧,则返回还能有返回值。

(注:由于VS并不会立刻清理掉栈帧,因此运行该程序,返回值会返回1

       通过上面的例子,可以得出一个结论,即:在函数运行结束后,即离开函数的作用域后,此时的返回对象被销毁,因此不能采用引用返回。

但是,对于静态变量和利用malloc类似方式创建的变量,在函数结束后不会销毁,可以采用引用返回。

3.2.2 引用返回的价值:

       由于用返回的价值主要体现在C++的后续内容,因此,本文在此部分指给出结论,原理会在后续的文章中进行阐述。

1. 提高返回速度  

2. 可以修改返回对象

3.3 常引用:

对于常引用,具体可以有下面代码反应:

int main()
{
	const int a = 1;
	int& b = a;
    return 0;
}

        从上面代码可以看到,变量a是一个被const修饰的常变量,下面对变量a进行了一次引用。当去运行上述代码时,编译器会显示错误。

      但是如果将上述代码进行修改,即:

int main()
{
	const int a = 1;
	const int& b = a;
	return 0;
}

       运行此代码时,并没有错误,不难观察到,上下的给的代码唯一的差距就是第二次给出的代码的引用前加了const。因此,在进行常引用的过程中,一定不能涉及变量权限的放大。例如上面运行失败的代码中,原本变量a的权限是不能修改,但是在引用后,由于引用的修改会引起原变量的修改,因此,引用的权限超过了原变量的权限,造成了权限放大。所以导致运行错误。这里需要注意的时,只是不能放大变量的权限,对于变量权限的平移的缩小均无影响,例如:
 

int main()
{
	int c = 2;
	const int& d = c;
	return 0;
}

      上述代码在引用后,缩小了变量的权限,可以正常运行。

      另外,对于一个常量进行常饮用也是允许的,即:

const int& e = 10;

令外,当引用与被引用的对象存在类型转换的情况时,即:

int j = 10;
	double h = 15;

	const double& k = j;

由于类型转化的过程中,会额外生成一个常量作用于该过程,引用时k接收的值并不是i,而是i通过一个常量完成类型转换间接传给k,由于常量的常性,因此需要加上const

4. 指针与引用的练习与区别:

     1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
     2. 引用在定义时必须初始化,指针没有要求
     3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何
一个同类型实体
    4. 没有NULL引用,但有NULL指针
    5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
位平台下占4个字节)
   6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
   7. 有多级指针,但是没有多级引用
   8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
   9. 引用比指针使用起来相对更安全

  10.在引用这一小节开头的时候说到,引用是针对一个对象起的别名,与被引用对象共用一块空间。在汇编层上,引用是利用指针来实现的,因此也需要开辟空间。不过,在日常运用时,一般认为引用是不会开辟新空间的。

5. 内联函数:

       对于一般的函数,在函数作用时需要开辟栈帧,而内联函数不需要开辟栈帧,而是在运行中原地展开。由于内联函数不需要开辟栈帧,因此提高了程序的运行效率。

      内敛函数的书写,只需要在函数声明的开头加上inline即可,例如:

inline int Add(int x, int y)
{
	int z = x + y;
	return x + y;
}
int main()
{
	int x = 5;
	int y = 1;
	int ret = Add(x, y);
	return 0;
}

            但是需要注意,内联函数并不能完全替代普通函数,并且,当一个函数的代码函数>10行时,不建议采用内联函数。这是因为,内联函数每次调用时都会原地展开,当调用的次数较大时,例如调用10000次,则一个代码行数为50行的内联函数完全展开后为500000行,延缓程序的运行速度。

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

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

相关文章

linux之信号

Linux之信号 什么是信号信号的产生方式signalsignactionkill信号集信号屏蔽 什么是信号 信号机制是一种使用信号来进行进程之间传递消息的方法&#xff0c;信号的全称为软中断信号&#xff0c;简称软中断。 信号的本质是软件层次上对中断的一种模拟&#xff08;软中断&#xff…

Hello Qt!

目录 1. 什么是Qt 2. Qt中的模块 3. 下载安装 4. QtCreator 4. Hello Qt 解释 .pro 解释 main.cpp 解释 mainwindow.ui 解释 mainwindow.h 解释 mainwindow.cpp 5. Qt 中的窗口类 5.1 基础窗口类 5.2 窗口的显示 6. Qt 的坐标体系 7. 内存回收 1. 什么是Qt 是一…

✔ ★【备战实习(面经+项目+算法)】 11.6 学习

✔ ★【备战实习&#xff08;面经项目算法&#xff09;】 坚持完成每天必做如何找到好工作1. 科学的学习方法&#xff08;专注&#xff01;效率&#xff01;记忆&#xff01;心流&#xff01;&#xff09;2. 每天认真完成必做项&#xff0c;踏实学习技术 认真完成每天必做&…

【斗破年番】萧炎给彩鳞承诺遭删,熏儿限时返场,古河沦为打工人

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料&#xff0c;在斗破年番69话的最新剧情中&#xff0c;一场感人至深的情感戏份被删减了。在原著中&#xff0c;萧炎曾向美杜莎承诺&#xff0c;他会集齐材料&#xff0c;为她炼制出天雁九行翼。然而&a…

简单2招GET模型参数量计算和输入尺寸随卷积大小变化推导

本文将介绍两种简单且实用的方法&#xff0c;用于计算深度学习模型的参数量&#xff0c;并推导了输入尺寸随卷积大小的变化过程。这些方法可以帮助读者更好地理解模型的复杂度和输入尺寸的变化&#xff0c;为模型设计和优化提供指导。 比如论文中&#xff0c;通常会比较几种模…

机器学习概论

一、机器学习概述 1、机器学习与人工智能、深度学习的关系 人工智能&#xff1a;机器展现的人类智能机器学习&#xff1a;计算机利用已有的数据(经验)&#xff0c;得出了某种模型&#xff0c;并利用此模型预测未来的一种方法。深度学习&#xff1a;实现机器学习的一种技术 2…

龙迅LT6911UXC替换LT6911UXE 支持单PORT 4K60HZ 带HDCP

LT6911UXC 描述&#xff1a; 龙迅LT6911UXC是一个高性能的HDMI2.0到MIPI DSI/CSI & LVDS转换器。HDMI2.0输入支持高达6Gbps的数据速率&#xff0c;这为60Hz的视频提供了足够的带宽。同时&#xff0c;还支持使用HDCP2.2来进行数据解密。对于MIPI DSI/CSI输出&#xff0c;LT…

【二进制转换和与其有关的操作符详解】

文章目录 1.二进制与进制转换2. 2进制转8、10、16进制2.1 2进制转10进制2.2 2进制转8进制2.3 2进制转16进制 3. 8、10、16进制转2进制3.1 10进制转2进制3.2 8进制转2进制3.3 16进制转2进制 4.原码、反码、补码5.移位操作符&#xff08;<< >>&#xff09;5.1左移操作…

英飞凌TLF35584规格书中文

官网&#xff1a; 英飞凌TLF35584QVVS2 TLF35584_SPI&#xff1a; 1 Overview2 Block Diagram3 Pin Configuration3.1 Pin Assignment - PG-VQFN-48 4 General Product Characteristics4.1 Absolute Maximum Ratings 绝对最大额定值4.2 Functional Range4.3 Thermal Resistance…

制造业企业设备管理常见的三个问题及对应的解决方案

当今的市场如同茫茫大海&#xff0c;既充满机遇&#xff0c;也伴随着波动的风险。在现代制造业中&#xff0c;企业常常面临着各种挑战&#xff0c;这些挑战可能妨碍其发展和竞争力。但制造企业往往具备能够解决挑战的能力&#xff0c;借助软件工具的力量&#xff0c;可以更好地…

vue3中router和route的区别(使用场景)

1.router router是用来对路由进行操作的&#xff1b; 多用于路由跳转、路由守卫、页面刷新、给路由文件添加路由路径或者移除路由路径等等 2.route route是用来获取路由信息的&#xff1b; 多用于获取路由路径、路由传参数据、路由文件配置的属性信息等等

2000-2022年“宽带Z国“试点城市名单匹配数据

2000-2022年“宽带Z国“试点城市名单匹配数据 1、时间&#xff1a;2000-2022年 2、指标&#xff1a;行政区划代码、年份、地区、所属省份、所属地域、试点城市、最早试点年份、DID 3、来源&#xff1a;来自工信部和国家发改委在2014年、2015年和2016年分别遴选的“宽带中国”…

x86汇编代码学习-计算机工作原理2

文章目录 前言1.mov2.内存分段3.调试5.注释6.标号7.add sub8.inc dec自增&#xff0c;自减9.adc sbb10 乘法栈11 栈寄存器push pop12 jz jnz13 section 16字节对齐14 CALL15 逻辑运算16 启动MBR以外的程序174. 前言 视频教程 x86汇编代码学习-计算机工作原理1 1.mov 因为ah是…

C++ 信息学奥赛 2048:【例5.18】串排序

#include<bits/stdc.h> using namespace std; int main() {string s[25];//string类数组 int n;cin >> n;for(int i 1; i < n; i)cin >> s[i];sort(s1, s1n);//默认升序 调用函数默认排序 for(int i 1; i < n; i)cout << s[i] << endl;…

Python实现Labelme的Json标注文件与YOLO格式的TXT标注文件相互转换

Python实现Labelme的Json标注文件与YOLO格式的TXT标注文件相互转换 前言前提条件相关介绍实验环境Labelme的Json标注文件与YOLO格式的TXT标注文件相互转换convert_labelme_json_to_txtjsons/000000000009.json代码实现输出结果labels/000000000009.txt convert_txt_to_labelme_…

Flink(一)【WordCount 快速入门】

前言 学完了 Hadoop、Spark&#xff0c;本想着先把 Kafka、Flume 这些工具先学完的&#xff0c;但想了想还是把核心的技术先学完最后再去把那些工具学学。 最近心有点累哈哈哈&#xff0c;偷偷立个 flag&#xff0c;反正也没人看&#xff0c;明年的今天来这里还愿哈&#xff0c…

C语言100~200中不能整除3的数

#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h> int main() {int n;for (n 100; n < 200; n){if (n%3 0){continue;}printf("%d\n",n);}}

RFC使用与WebService

RFC连接 CSDN RFC中引用类型组 http://t.csdnimg.cn/wQWAYhttp://t.csdnimg.cn/wQWAY 远程目标系统维护SM59 这里的类型指的是目标系统的系统类型(目标系统即rfc函数存在的系统). 类型2&#xff08;R/2连接&#xff09;&#xff0c;只需给出主机名&#xff0c;所有通信信息…

软件系统设计方法和工具介绍

软件系统设计方法和工具介绍 在构建系统时&#xff0c;尤其是一些大项目实施的过程中&#xff0c;可以接触和学习一些高阶层面分析问题和系统架构的方法论&#xff0c; 如麦肯锡的解决问题7步法&#xff1a;定义问题、分解问题、排定优先级、制定工作计划、分析问题、综合分析…