C++入门2

news2024/11/15 15:53:10

函数重载

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

比如下面的

int add(int x, int y)
{
	cout << "int add(int x ,int y)" << endl;
	return x + y;
}

double add(double x, double y)
{
	cout << "double add(double x,double y)" << endl;
	return x + y;
}

//参数个数不同

void func()
{
	cout << "func()" << endl;

}
void func(int x)
{
	cout << "func(int x)" << endl;
}

//参数顺序不同

void f(int x, char y)
{
	cout << "f(int x,char y)" << endl;
}


void f(char y, int x)
{
	cout << "f(char y,int x)" << endl;
}
int main()
{
	 add(1, 2);
	add(1.1, 1.2);

	func();
	func(2);

	f(1, 1.2);
	f(1.2, 1);
	return 0;
}

函数运行结果

值得注意的是,函数重载的定义是在同一个定义域里面的,如果在不同的作用域里面的话,就不算是函数的重载,下面的就不算是函数的重载

namespace LH
{
    int add(int x, int y)
    {    
	    cout << "int add(int x ,int y)" << endl;
	    return x + y;
    }
}

double add(double x, double y)
{
	cout << "double add(double x,double y)" << endl;
	return x + y;
}

 函数重载的一些注意事项

1.不同作用域里面的函数可以一样

namespace LH1
{
	void add()
	{
		cout << "LH1::add()" << endl;
	}
}
namespace LH2
{
	void add()
	{
		cout << "LH2::add()" << endl;
	}
}

//using namespace LH1
//using namespace LH2
//加上这两句话在调用函数的时候虽然会冲突,但是依然不影响他们是重载的关系
int main()
{
	LH1::add();
	LH2::add();
	return 0;
}

 当函数是缺省参数的时候

//这两个函数虽然是重载函数,但是在调用的时候也会冲突
void f()
{
	cout << "f()" << endl;
}

void f(int a = 10)
{
	cout << "f(int a)" << endl;
}

C++支持重载,C语言不支持

在C语言当中,是不支持重载的,但是在C++中,是支持重载的

这涉及到编译链接的过程,下面用一个图来解释

在上面的test.o文件里面,包含的有Add.h里面的东西,也就是函数的申名,在链接的过程中,有了函数的申明,编译器会找函数的定义,也就是在.CPP文件去找函数的地址(这里函数的地址实际上就是函数的语句)

由于C语言和C++找函数的方式不同

C语言用的是函数名去找,而C++是用被修饰的函数名字去找(也就是包含函数里面的参数),C语言用函数的名字去找,有多个函数名就会报错,但是C++不会

引用

1. 引用在定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体

引用实际上就是给变量取别名,对别名进行操作也会引起自身的变化

int main()
{
    //整形变量的别名
    int a=10;
    int&b=a;    //给a取一个别名,相当于给a取一个外号


    //指针变量的别名
    int x = 0;
    int* p1 = &x;
    int*& p2 = p1;
    retrun 0;


}

权限的平移,缩小,放大

// 权限的平移,可以
int x = 0;
int& y = x;
y++;

	
//权限的缩小,可以
int x=0;
const int& z = x;
z++;       // 不可以


	// 权限的方法
	// m只读
	// n变成我的别名,n的权限是可读可写
	// 权限的放大,不可以
	const int m = 0;
	// int& n = m;

	const int& n = m;

	// 可以,不是权限的放大
	// m拷贝的给p,p的修改不影响m
	int p = m;

	// 权限的放大
	// p1可以修改 *p1不可以,const修饰是的*p1
	const int* p1 = &m;
	// p1++;
	// int* p2 = p1;
	const int* p2 = p1;

	// 权限的缩小
	int* p3 = &x;
	const int* p4 = p3;

引用作为函数参数

引用也可作为函数参数来代替指针的一些操作(形参是实参的引用)

void Swap(int& x, int& y)
{
	int temp = x;
	x = y;
	y = temp;
}

int main()
{
	int a = 10;
	int b = 20;
	Swap(a, b);
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
	return 0;
}

输出结果

传值和传引用效率的比较

值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直
接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效
率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{
	A a;
	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc1(a);
	size_t end1 = clock();
	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc2(a);
	size_t end2 = clock();
	// 分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

int main()
{
	TestRefAndValue();
	return 0;
}

运行结果

通过上述代码的比较,发现传值和指针在作为传参以及返回值类型上效率相差很大 

引用和指针的区别

语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。

int main()
{
	
		int a = 10;
		int& ra = a;
		cout << "&a = " << &a << endl;
		cout << "&ra = " << &ra << endl;
		return 0;
	
}

运行结果

从结果上面来看,引用别的变量是和别的变量用同一块空间

但是在从底层来看,引用和指针一样,都是用指针的方式实现的

int main()
{
int a = 10;
int& ra = a;
ra = 20;
int* pa = &a;
*pa = 20;
return 0;
}

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

内联函数

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调
用建立栈帧的开销,内联函数提升程序运行的效率

上面的call是调用函数地址的意思

内联函数的特性 

1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会
用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
行效率。
2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不
是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性

3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址
了,链接就会找不到

// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{
f(10);
return 0;
}
// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl
f(int)" (?f@@YAXH@Z),该符号在函数 _main 中被引用

auto关键字

auto关键字是根据等式左边的数据类型来确定变量的类型的

auto a=1;  //右边是整形,左边自动得出a是整形变量

这个关键字现阶段还没有什么意义,但是等到等式右边的变量是一个很长的类型的时候,就会很有意义

std::map<std::string, std::string> m{ { "apple", "苹果" }, { "orange",
"橙子" },
 {"pear","梨"} };
//std::map<std::string, std::string>::iterator it = m.begin();

 //换成下面的这个

auto it=m.begin();

 std::map<std::string, std::string>::iterator 是一个类型,但是该类型太长了,特别容
易写错。

可以通过typedef给类型取别名,比如

#include <string>
#include <map>
typedef std::map<std::string, std::string> Map;
int main()
{
Map m{ { "apple", "苹果" },{ "orange", "橙子" }, {"pear","梨"} };

Map::iterator it = m.begin();
while (it != m.end())
{
//....
}
return 0;
}

 使用typedef给类型取别名确实可以简化代码,但是typedef有会遇到新的难题

typedef char* pstring;
int main()
{
const pstring p1;   // 编译成功还是失败?  失败 需要给值
const pstring* p2;  // 编译成功还是失败?  成功
return 0;
}

8.2 auto简介
 

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的
是一直没有人去使用它,大家可思考下为什么?
C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一
个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

int TestAuto()
{
return 10;
}
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = TestAuto();
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
return 0;
}

 使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto
的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编
译期会将auto替换为变量实际的类型。

 auto的使用细则

1. auto与指针和引用结合起来使用
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须
加&

int x = 10;
  auto a = &x;
  auto* b = &x;    //如果有*,右边必须是一个指针变量
  auto& c = x;

 在同一行定义多个变量

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译
器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

void TestAuto()
{
  auto a = 1, b = 2;
  auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}

 

auto不能推导的场景 

 1,auto不能作为函数的参数

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}

auto不能直接用来声明数组

void TestAuto()
{
  int a[] = {1,2,3};
  auto b[] = {4,5,6};
}

 遍历数组

auto还可以用来遍历数组(范围for)

oid TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)              //不加&是不会改变数组里面的值的
  e *= 2;
for(auto e : array)
  cout << e << " ";
return 0;
}
void TestFor(int array[])
{
  for(auto& e : array)
    cout<< e <<endl;
}

范围for的使用条件

1. for循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供
begin和end的方法,begin和end就是for循环迭代的范围。
注意:以下代码就有问题,因为for的范围不确定

void TestFor(int array[])
{
  for(auto& e : array)
    cout<< e <<endl;

}

空指针

在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现
不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下
方式对其进行初始化: 

void TestPtr()
{
int* p1 = NULL;
int* p2 = 0;
// ……
}

NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL
#ifdef __cplusplus
#define NULL  0
#else
#define NULL  ((void *)0)
#endif
#endif

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何
种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如

void f(int)
{
cout<<"f(int)"<<endl;
}
void f(int*)
{
cout<<"f(int*)"<<endl;
}
int main()
{
f(0);                 输出:第一个
f(NULL);                输出:第一个
                        
f((int*)NULL);            输出:第二个
return 0;
}

 程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的
初衷相悖。
在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器
默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void
*)0。

注意:
1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入
的。
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

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

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

相关文章

数据结构和算法|递归算法那些事(递归算法的时间复杂度、尾递归优化、斐波那契数列)

对于文章的第一部分&#xff0c;递归算法的时间复杂度&#xff0c;来自于代码随想录文章:通过一道面试题目&#xff0c;讲一讲递归算法的时间复杂度&#xff01; 对于第二节尾递归优化来自于B站&#xff1a;尾递归优化&#xff1a;你的递归调用是如何被优化的&#xff1f; 文章…

Spring Boot - 通过ApplicationListener实现接口请求的性能监控

文章目录 概述1. ServletRequestHandledEvent事件2. 实现步骤3. 优缺点分析4. 测试与验证小结其他方案1. 自定义拦截器2. 性能监控平台3. 使用Spring Boot Actuator4. APM工具 概述 在Spring框架中&#xff0c;监控接口请求的性能可以通过ServletRequestHandledEvent事件实现。…

【Java日志系列】日志概述

目录 前言 一、日志概述 二、日志文件 1. 调试日志 2. 系统日志 三、日志框架 1. 日志框架的作用 2. 日志框架的价值 3. 市面上流行的日志框架 4. 日志门面和日志实现的区别 总结 前言 在软件开发中&#xff0c;日志记录是一项至关重要的任务。无论是简单的命令行应…

微服务架构-SpringCloud

1.单体应用架构 将项目所有模块(功能)打成jar或者war&#xff0c;然后部署一个进程。 优点&#xff1a; 部署简单&#xff1a;由于是完整的结构体&#xff0c;可以直接部署在一个服务器上即可。技术单一&#xff1a;项目不需要复杂的技术栈&#xff0c;往往一套熟悉的技术栈就…

Netty技术全解析:LineBasedFrameDecoder类深度解析

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…

【leetcode】杨辉三角(Java语言描述)

杨辉三角 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]示例 2: 输入: numRows 1 输出: [[1]] …

XIAOJUSURVEY 重磅升级,推出图形化逻辑编排能力

&#x1f389;&#x1f389;&#x1f389;XIAOJUSURVEY 是滴滴开源的企业级问卷系统&#xff0c;现已重磅升级&#xff01;&#xff01;&#xff01; 本次升级基于自研规则引擎&#xff0c;全面支持复杂的问卷逻辑编排&#xff0c;实现了显示逻辑与跳转逻辑的无缝整合。突破了…

MySQL Galera Cluster 部署与介绍

目录 主要特点 组件 一. 环境准备 二. 配置 1. 配置 galera1 主机的my.cnf的文件 2. 配置 galera2 主机的my.cnf的文件 3. 配置 galera3 主机的my.cnf的文件 4. 在给galera1 主机的my.cnf的文件增加节点 5. 写入数据验证同步 6. 配置 galera4 主机的my.cnf的文件 M…

线性回归Pytorch方法

借助 PyTorch 实现深度神经网络 - 线性回归 PyTorch 方法 - 第 3 周 | Coursera 随机梯度下降和数据加载器 在每个epoch中&#xff0c;使用一个样本进行更新的方法称为随机梯度下降&#xff0c;而使用所有样本进行更新的方法称为批量梯度下降。 随机梯度下降&#xff1a; 随…

第五代数字产业园入驻西安,西安国际数字影像产业园究竟能带来哪些颠覆性变革?

西安&#xff0c;这座承载着千年历史文化底蕴的古老城市&#xff0c;在时代的洪流中不断焕发出新的生机与活力。如今&#xff0c;西安第五代数字产业园入驻西安&#xff0c;犹如一颗璀璨的新星在这片土地上冉冉升起&#xff0c;为这座城市的发展注入了强大的动力。而在这一重大…

AFSim 仿真系统---- 通信、传感器与干扰系统方程

概述 本文档旨在描述在WSF中对象之间交互所使用的方程和算法。这包括&#xff1a; 传感器交互通信交互干扰&#xff08;干扰&#xff09;交互 常见无线电频率方程 WSF使用一套通用类来封装涉及无线电频率&#xff08;RF&#xff09;交互的组件&#xff08;实际上&#xff0…

基于B/S模式的JSP二手车交易网站

你好&#xff0c;我是计算机专业的学生&#xff0c;专注于Web开发。希望我的论文能够为您提供一些有价值的信息。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;ECLIPSE、Tomcat 系统展示 首页 用户界面 管理员界面 汽车评…

新加坡服务器延迟大吗?如何进行优化

新加坡服务器延迟大吗&#xff1f;新加坡服务器的延迟通常在全国平均延迟111ms左右&#xff0c;其中移动网络约为90ms&#xff0c;联通网络106ms&#xff0c;电信网络最低约为85ms。为了进行优化&#xff0c;一般可以采取使用CDN、优化路由线路、增加带宽和服务器升级等方法。 …

VBA高级应用30例应用3在Excel中的ListObject对象:创建表

《VBA高级应用30例》&#xff08;版权10178985&#xff09;&#xff0c;是我推出的第十套教程&#xff0c;教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开&#xff0c;这套教程案例与理论结合&#xff0c;紧贴“实战”&#xff0c;并做“战术总结”&#xff0c;以…

Linux 内核源码分析---简单文件系统

顺序文件 debugfs 文件系统 允许只用几个函数调用&#xff0c;就实现一个双向的调试接口。 小的文件系统中的文件&#xff0c;通常用户层是 从头到尾顺序读取 的&#xff0c;其内容可能是遍历一些数据项创建的。 kprobe 机制包含了到上述 debugfs文件系统 的一个接口。一个顺…

【模电笔记】——集成运算放大电路

tips&#xff1a;本章节的笔记已经打包到word文档里啦&#xff0c;建议大家下载文章顶部资源&#xff08;有时看不到是在审核中&#xff0c;等等就能下载了。手机端下载后里面的插图可能会乱&#xff0c;建议电脑下载&#xff0c;兼容性更好且易于观看&#xff09;&#xff0c;…

利用学校资源访问scientdirect等国外数据库

就直接点view pdf这样的下载按钮&#xff0c;会让你输入组织&#xff0c;搜索找到学校的名字&#xff0c;接下来可以能会要求输入学校邮箱或者其他身份认证信息&#xff0c;最后就可以享受学校已经出过钱买的服务啦&#xff01;如果不用那么你每个学期大几千的学费简直是暴殄天…

定制你的项目工作流:最适合的文档管理解决方案

国内外主流的10款项目文档管理软件对比&#xff1a;PingCode、Worktile、Teambition、Tapd、Tower、Confluence、Notion、Dropbox Paper、Quip、Basecamp。 在面对项目管理的复杂性时&#xff0c;选择合适的文档管理工具可以显著提高效率和团队协作。许多团队在文档管理上遭遇混…

灰度宣布推出SUI加密投资信托基金

灰度&#xff0c;这家推出受欢迎的BTC和ETH交易所交易基金&#xff08;ETF&#xff09;的投资公司&#xff0c;于8月7日宣布将开始提供两种新的加密基金Grayscale Sui Trust和Grayscale Bittensor Trust&#xff0c;这些基金投资于Layer-1区块链Sui&#xff08;SUI&#xff09;…

RTT-网络组件-AT命令-未完成

AT指令文档 调用树 at_client_init();at_client_para_init();client_parser();struct at_client {rt_device_t device;at_status_t status;char end_sign;char *send_buf;/* The maximum supported send cmd length */rt_size_t send_bufsz;/* The length of last cmd */rt_si…