C++入门(命名空间、缺省参数、函数重载、引用、内联函数)

news2024/9/23 3:23:09

全文目录

  • 引言
  • C++输入与输出
  • 命名空间
    • 概念
    • 使用
      • 使用域作用限定符::使用某个成员
      • 使用using namespace 引入整个命名空间域
      • 使用using引入某个成员
  • 缺省参数
    • 概念
    • 分类
  • 函数重载
    • 定义与调用
    • 原理
  • 引用
    • 定义
      • 需要注意
    • 使用
      • 引用作为返回型参数
      • 引用作为返回值
    • 引用与指针的区别
  • 内联函数
  • 总结

引言

C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机,20世纪80年代,计算机界提出了OOP(object oriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。
1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。

在前面对C语言的学习中,相信大家对用C语言编程已经很了解了,所以就不赘述基础的语法了。在本篇文章中将介绍一些新知识,可以让你初步上手C++:

C++输入与输出

首先要了解的就是C++的输入与输出:

cout是标准输出对象,用于向标准输出流(屏幕)写入数据;
endl是一个操纵符,写入endl的效果为换行,并将缓冲区中的数据刷新到设备中;
使用输出运算符 << 可以在标准输出上打印信息:

std::cout << 表达式1 << 表达式2 << sts::endl;

cin是标准输入对象,用于从标准输入流(键盘)读取数据;
使用输入运算符>> ,从给定的输入流中读取数据放到给定的对象中:

std::cin >> 变量1 >> 变量2;

使用cin与cout输入输出时,可以自动识别输入或输出的类型,而且不需要取地址的操作。相较于C语言的需要写出输入输出的格式。

使用cin、cout、endl时需要包含头文件<iostream>,并且由于它们的实现都在C++标准库命名空间std中,所以使用时需要引入命名空间或域作用限定符使用。

命名空间

接下来就介绍关于命名空间的知识:

在C语言中,经常会遇到自己定义的变量或函数与库中定义的函数命名冲突的情况,或是在项目中自己定义的函数与别人定义的函数命名冲突的情况,这样的情况往往令人厌烦。
使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染

概念

定义命名空间时,要用到关键字namespace,后面加命名空间的名称,然后在后面的{}中定义成员(变量或函数等):

namespace qqq
{
	int rand = 0;
	int max(int a = 0, int b = 0)
	{
		if (a > b)
		{
			return a;
		}
		return b;
	}
}

在上面的命名空间qqq中定义的rand变量与max函数,这两个符号都是在库中实现的函数,在命名空间qqq中定义这两个字符,就相当于将这个rand与max放在命名空间域中,而不是暴露在全局域,就不会发生命名冲突。

命名空间可以嵌套,也可以定义多个名称相同的命名空间(编译时会和为一个)。

使用

使用命名空间中的成员变量或成员函数时,有三种方式:

使用域作用限定符::使用某个成员

使用域作用限定符::使用某个成员时,每次使用某个在命名空间中的成员时,都要显式的写出命名空间名::成员名

//使用域作用限定符::使用某个成员
//省略头文件包含与命名空间qqq
int main()
{
	int a = qqq::rand;
	int b = 10;
	std::cout << qqq::max(a, b);
	return 0;
}

使用using namespace 引入整个命名空间域

使用using namespace 命名空间;名引入整个命名空间域时,就相当于将整个命名空间域全部暴露在全局域中,命名空间中的成员就成了全局的。使用时就可以直接使用:

//使用using namespace 引入整个命名空间域
//省略头文件包含与命名空间qqq
using namespace qqq;
using namespace std;
int main()
{
	int a = rand;
	int b = 10;
	cout << max(a, b);
	return 0;
}

使用using引入某个成员

上面的方式虽然方便,但是将命名空间的成员暴露在全局,就使命名空间丧失了其原有的意义。但是对于某些使用频繁的函数,显式的说明又太繁琐,所以我们可以使用using 命名空间名 :: 成员名;的方式来引入某个成员,相当于将这个成员暴露在全局,后面在使用这个成员时可以直接使用,但别的成员依旧需要显式说明:

//使用using引入某个成员
//省略头文件包含与命名空间qqq
using qqq::max;
using std::cout;
int main()
{
	int a = qqq::rand;
	int b = 10;
	cout << max(a, b);
	return 0;
}

对于上面提到的cin、cout、endl,都在标准库命名空间std中实现,在使用时当然可以直接展开标准库:using namespace std;,也可以每次使用时都std::,但是最安全便捷的方法就是引入常用的成员:using std::cout; (例如上)。
除此之外,所有的库函数都在std中。

缺省参数

在需要对对象初始化时,相对于C语言中的#define定义一个常量,用缺省参数设置一个默认值的方式要方便许多。

概念

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

#include<iostream>
void add(int a = 1, int b = 2)
{
	std::cout << a + b << std::endl;

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

在这里插入图片描述

需要注意的是:为了避免定义与声明的默认值不同,函数的定义与声明不能同时设置默认值。

分类

根据参数列表是否完全设置默认值,分为全缺省与半缺省:

全缺省参数就是参数列表全部设置默认值。
在调用全缺省参数的函数时,如果传参,实参会从左到右依次给参数列表中的形参赋值,不能跳着赋值:

#include<iostream>
void Func(int a = 1, int b = 2, int c = 3)
{
	std::cout << a << " " << b << " " << c << std::endl;
}
int main()
{
	//Func(10, , 30);错误调用
	Func();//正确调用
	Func(10, 20);
	return 0;
}

在这里插入图片描述

半缺省参数就是参数列表不全部设置默认值。
半缺省参数只能从右向左依次给出默认值;调用时的实参不能少于未给默认值的参数个数,也是从左向右依次赋值给实参:

#include<iostream>
//void Func(int a = 1, int b, int c = 3){}错误定义
void Func(int a, int b = 2, int c = 3)
{
	std::cout << a << " " << b << " " << c;
}
int main()
{
	//Func();错误调用
	Func(10);//正确调用
	return 0;
}

在这里插入图片描述

函数重载

在实现函数时,经常会有一些函数的作用类似,只是参数的类型有所不同。如果定义几个函数名不同但功能类似的函数,在调用时就会不方便。就有了函数重载:

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

定义与调用

在定义函数重载时,函数名相同,参数个数不同、参数类型不同、参数类型顺序不同都可以构成重载:

int Func(int a, int b = 20)
{
	cout << 1 << endl;
	return 1;
}
int Func(double a, int b)//与第一个函数参数类型不同
{
	cout << 2 << endl;
	return 2;
}
int Func(int a, double b)//与第二个函数参数顺序不同
{
	cout << 3 << endl;
	return 3;
}
int Func(int a)//与第一个函数参数个数不同
{
	cout << 4 << endl;
	return 4;
}

需要注意的是:当参数列表相同时,返回值的不同不构成重载

//错误代码,会报错:无法重载仅按返回类型重载的函数
int Func(int a, int b = 20)
{
	cout << 1;
	return 1;
}
void Func(int a, int b)
{
	cout << 1;
}

在这里插入图片描述
调用重载的函数时,由于函数有缺省参数,比如当调用时只传一个参数时,就不知道要调用第一个参数没有默认值的函数,还是只有一个形参的重载函数,还是其他可以只传一个参数的函数了。所以需要特别注意避免出现二义性:

Func(10);//错误调用。对于传一个整型,第1个函数与第4个函数均可

在这里插入图片描述
正常情况下,可以根据传递的参数列表来调用相应的函数:

Func(10, 30);
Func(1.1, 5);
Func(5, 1.1);

在这里插入图片描述

原理

那么为什么C++支持函数重载?

在C语言进阶部分介绍过,我们编写的代码在转化为可执行程序之前,要经历:预编译、编译、汇编、链接的过程。
在编译时,会生成符号表,符号表包括符号名与地址。根据函数名修饰规则,每一个不同的函数都会有自己独特的符号,当出现两个符号相同的函数时,就会报编译错误(即符号重定义)。

只是在C语言编译器修饰后,符号名就是函数名同名的函数就会重定义;而C++的的编译器修饰后的符号名中会包含函数名、参数列表的信息。重载的函数符号名是不同的,不会重定义,调用时虽然函数名相同,但其实调用的是不同的函数。
但是由于修饰规则中没有包含返回值类型,所以函数名与参数列表相同但返回值不同的函数符号名也是相同的,所以不能构成重载。

引用

引用就是起别名,因为它与其引用的变量本质上同用一块空间,所以可以通过引用变量改变其引用的变量的值,而且在语法上不开辟新的空间。

定义

在类型后加 & 就可以定义一个引用变量:类型& 引用变量名 = 引用实体;

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

在这里插入图片描述

在上面的代码中,定义了整型变量a,引用变量ra是a的别名,rra是ra的别名,即rra也是a的别名。它们三个名称共用一块空间,当改变其中一个名称的值时,另外两个的值也会发生改变。

需要注意

在定义引用变量时,一定要注意一些问题:

  1. 引用在定义时必须初始化,即必须明确是谁的引用:
int& a;//错误代码,必须初始化引用
  1. 一个实体可以有多个引用,但一个引用变量只能引用一个实体:
int a = 10;
int b = 5;
int& ra = a;
int& rra = ra;
//int& ra = b; 错误代码,ra重定义
  1. 引用时权限只能平移或缩小,不能放大:

我们定义一个引用变量,就可以通过这个引用来改变实体的值。但是对于具有常属性的实体变量,给它起一个可以改变其内容的别名显然是非常危险的。
所以对于有常属性的变量,只能给常引用初始化(即引用权限不能放大):

const int a = 10;
//int& ra = a; 错误代码,权限放大
const int& ra = a; //正确代码

当然,如果给一个非常量定义常引用,即权限的缩小或平移,是可以的:

int b = 5;
const int& rb = b;//正确代码,权限缩小
int& rrb = b;//正确代码,权限平移

不仅是const修饰的变量,传值的返回值、发生类型转换的变量都具有常属性。因为它们在转换的时候会隐式拷贝一份到临时变量,再由临时变量赋值,而这个临时变量是有常属性的。

  1. 引用类型与其实体类型必须相同:
int c = 2;
//double& rc = c; 错误代码,引用与实体的类型不同

使用

引用作为返回型参数

在之前,对于一些需要返回型参数的函数实现,我们只能使用指针传参,这样不论在传参还是调用时都很麻烦。而用引用作为返回型参数就会很方便,比如这个交换函数:

//命名空间展开与头文件包含已省略
void Swap(int& a, int& b)
{
	int temp = b;
	b = a;
	a = temp;
}
int main()
{
	int a = 10;
	int b = 20;
	Swap(a, b);
	cout << a << " " << b;
	return 0;
}

在这里插入图片描述
传参时不用取地址,交换时不用解引用,看起来也很清楚,更适合做返回型参数。

引用作为返回值

在之前的返回值只能传值返回,或是传指针返回。传值返回时,是有隐式的拷贝过程的,对于大对象而言非常影响效率;传指针返回写起来也不方便。传引用返回就既效率高又直观:

在这里插入图片描述
在这里插入图片描述
这样的代码,看起来没有问题,也实现了Add的作用,但是却有一个很大的问题。就是传引用返回了一个局部变量。这种行为是很危险的,因为局部变量在生命周期结束后,空间就会还给操作系统,返回的这个引用就是一个不确定数据的别名(如果环境会清除数据,就会返回任意值)。

所以在使用引用返回时需要特别注意,引用返回的值一定不能出了作用域就被销毁,可以返回静态变量等:

int& Add(int a, int b)
{
	static int c = a + b;
	return c;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	cout << c;
	return 0;
}

在这里插入图片描述

引用与指针的区别

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

需要注意的是:引用虽然在语法上没有开辟新的空间,但在底层逻辑上与指针是一致的,也需要开辟空间存放地址。

内联函数

在C语言部分,对于简单且调用频繁的函数,我们可以使用宏函数的方式,在预编译阶段就宏替换,而节约函数开辟栈帧的时间与空间以提高效率。比如宏函数ADD:

#define ADD(a, b) ((a)+(b))

由于宏函数是直接替换,而不是传参,想要宏函数的逻辑没有问题,写法显然是有些繁琐的。

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

inline int Add(int a, int b)
{
	return a + b;
}

需要注意的是:

  1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段会用函数体替换函数调用。缺陷:可能会使目标文件变大;优势:少了调用开销,提高程序运行效
  2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小、非递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性
  3. 内联函数的声明与变量不要分离(分文件)。由于内联函数是直接替换,没有函数地址,如果分离,就会无法替换,而且不能根据函数指针调用,就会报错;

总结

到此,关于C++入门的一些知识就介绍完了,以后就会持续更新C++的知识,欢迎大家持续关注
如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

C++之路才刚刚开始,我们一起加油吧!

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

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

相关文章

华为OD机试真题 Java 实现【猜字谜】【2023Q2】

一、题目描述 小王设计了一人简单的清字谈游戏&#xff0c;游戏的迷面是一人错误的单词&#xff0c;比如nesw&#xff0c;玩家需要猜出谈底库中正确的单词。猜中的要求如 对于某个谜面和谜底单词&#xff0c;满足下面任一条件都表示猜中&#xff1a; 变换顺序以后一样的&…

np保存数据为txt或者csv格式

目录 1、基础参数 2、参数详解 2.1、fmt 2.2、delimiter 2.3、newline 2.4、header 1、基础参数 numpy.savetxt(fname,arrry,fmt%.18e,delimiter ,newline\n,header,footer,comments# ,encodingNone,) 2、参数详解 fname:要存入的文件、文件名、或生成器。arrry:要存储…

xxl-Job分布式任务调度 入门

1.概述 1.1 什么是任务调度 我们可以先思考一下业务场景的解决方案&#xff1a; 某电商系统需要在每天上午10点&#xff0c;下午3点&#xff0c;晚上8点发放一批优惠券。 某银行系统需要在信用卡到期还款日的前三天进行短信提醒。 某财务系统需要在每天凌晨0:10结算前一天的…

C高级第二天

#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,const char *argv[]) { int n 0, m 0, MAX 0; int arr[n][m]; printf("请输入矩阵行数、列数>>>"); scanf("%d%d", &n…

【动态规划】线性DP

目录 一&#xff1a;思考方式 二&#xff1a;例题 例题1&#xff1a;数字三角形 例题二&#xff1a;最长上升子序列​​​​​​​ 例题三&#xff1a;最长公共子序列 一&#xff1a;思考方式 线性dp就是一条线上的动态规划 二&#xff1a;例题 例题1&#xff1a;数字三…

Python基础(三)

目录 1、Python的输入函数input() 1、input函数介绍 1.1作用&#xff1a; 1.2返回值类型&#xff1a; 1.3值得存储&#xff1a; 2、input函数的基本使用 2、Python中的运算符 2.1算术运算符 2.1.1标准算术运算符 2.1.2取余运算符(%) 2.1.3幂运算符(**) 2.1.4特殊运…

分布式锁的多种实现方式

1、不使用分布式锁 synchronized (this){int stock Integer.parseInt(Objects.requireNonNull(stringRedisTemplate.opsForValue().get("stock")));if (stock > 0) {int realStock stock - 1;// 更新库存stringRedisTemplate.opsForValue().set("stock&qu…

vi编辑器的三种模式及其对应模式下常用指令

vi是Linux系统的第一个全屏幕交互式编辑工具&#xff0c;在嵌入式的 学习中是一个不可或缺的强大的文本编辑工具。 一、三种模式 命令模式 如何进入命令模式&#xff1a;按esc键 复制&#xff1a;yy nyy(n&#xff1a;行数) 删除(剪切): dd ndd 粘贴&#xff1a;p 撤销&…

【Java】java | 将可运行jar打包成exe可执行文件

一、说明 1、javafx桌面程序&#xff0c;但又不想安装jre环境 2、需要将可执行jar打包成exe 3、使用工具exe4j 二、操作步骤 1、下载exe4j https://exe4j.apponic.com/ 2、安装 说明1&#xff1a; 在d盘建个exe4j的文件夹 说明2&#xff1a; 建个output文件jar&#xff0c;存放…

计算机组成原理——计算机系统的组成

一台完整的计算机包括硬件和软件两部分&#xff0c;另外还有一部分固话的软件成为固件(Frimware)&#xff0c;固件兼具软件和硬件的特性&#xff0c;常见的如个人计算机中的BIOS&#xff0c;BIOS&#xff08;Basic Input/Output System&#xff09;是个人计算机上的一个基本输入…

React 路由

React 的路由跳转需要引用第三方的 React Router npm i react-router-dom5.2.0 React Router 分为 BrowserRouter 和 HashRouter 如果我们的应用有服务器响应 web 的请求&#xff0c;建议使用<BrowserRouter>组件; 如果使用静态文件服务器&#xff0c;建议使用<Hash…

[golang gin框架] 29.Gin 商城项目-用户登录,注册操作

一.用户登录,注册界面展示说明 先看登录,注册界面以及相关流程,再根据流程写代码,一般网站的登录,注册功能都会在一个页面进行操作,还有的是在几个页面进行操作,这里讲解在几个页面进行注册的操作,步骤如下: 登录: 1.点击 登录按钮,进入登录界面 2.在登录界面输入手机号,密码,图…

Linux内核中与“文件系统”相关的数据结构

文件系统相关的数据结构 4.1 file结构体 文件结构体代表一个打开的文件&#xff0c;系统中的每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建&#xff0c;并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后&#xff0c;内核释放这…

【Flink】DataStream API使用之源算子(Source)

源算子 创建环境之后&#xff0c;就可以构建数据的业务处理逻辑了&#xff0c;Flink可以从各种来源获取数据&#xff0c;然后构建DataStream进项转换。一般将数据的输入来源称为数据源&#xff08;data source&#xff09;&#xff0c;而读取数据的算子就叫做源算子&#xff08…

【vue3】06-vue的组件化开发-脚手架创建项目

文章目录 Vue的组件化组件化开发注册组件的方式vue全局组件vue局部组件 Vue的开发模式Vue CLI脚手架安装Vue CLI使用Vue CLI Vue的组件化 Vue是一款前端框架&#xff0c;在这个框架中&#xff0c;组件化开发是非常重要的。Vue的组件化就是将一个页面划分为多个独立的、可复用的…

LeetCode5. 最长回文子串

写在前面&#xff1a; 题目链接&#xff1a;LeetCode5. 最长回文子串 编程语言&#xff1a;C 题目难度&#xff1a;中等 一、题目描述 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例…

算力256TOPS,典型功耗35W,存算一体芯片杀入智能驾驶

作者 | 张祥威 编辑 | 德新 国产智驾芯片有了新玩家 “最高物理算力256 TOPS&#xff0c;典型功耗35W&#xff0c;基于12nm制程工艺。” 5月10日&#xff0c;后摩智能发布首款基于存算一体架构的智驾芯片——鸿途™H30&#xff0c;并公布上述关键指标。 算力、数据和算法&am…

单例模式的饿汉和懒汉写法(基于C++)

目录 单例模式例程饿汉懒汉 对比函数调用线程安全总结 单例模式 单例模式确保一个类只有一个实例&#xff0c;并提供全局访问点。这样可以避免在系统中出现多个相同的对象&#xff0c;从而提高系统的性能和可维护性。 单例模式的实现包括饿汉和懒汉&#xff0c;下面介绍C中这两…

操作系统基础知识之处理器性能方程指标(包含阿达姆定律、CPI、Clock cycle time等)

计算机设计人员通过持续时间或速率来指代时钟周期的时间。程序的 CPU 时间可以用两种方式表示&#xff1a; CPU 时间程序的 CPU 时钟周期 / 时钟频率 除了执行程序所需的时钟周期数外&#xff0c;我们还可以计算执行的指令数。 如果我们知道时钟周期数和指令数&#xff0c;就…

金融学第二版笔记第一章1.1

第1部分 金融和金融体系 第一章金融学 1.1 一、 对金融学进行界定 1.金融 金融是货币流通、信用活动及与之相关的经济行为的总称。 简言之&#xff0c;就是货币资金的融通。一般是指以银行、证券市场等为中心的货币流通和信用调节活动&#xff0c;包括货币的发行和流通、存…