C++基础-命名空间-缺省参数-函数重载-引用-内联-auto-范围for

在这里插入图片描述

C++基础:

  • 1. 命名空间
    • 命名空间如何定义
    • 命名空间如何使用
  • 2.缺省参数
    • 2.1 缺省参数分类
      • 2.1.1全缺省参数
      • 2.1.2 半缺省参数
  • 3. 函数重载
  • 4. 引用
    • 4.1 常引用
    • 4.2 引用使用场景
      • 4.2.1 做参数
      • 4.2.2 做返回值
      • 4.2.3 传值和传引用效率分析
    • 4.3 引用和指针的区别
    • 4.4 指针和引用总结
  • 5. 内联函数
    • 5.1 特性
    • 5.2 面试题
  • 6.auto关键字
    • 6.1 auto使用方法
    • 6.2 auto不能使用的场景
  • 7.基于范围for的循环表达式
    • 7.1 范围for使用方法

1. 命名空间

在C/C++中,变量、函数和类都是大量存在的,它们的名称都将存在于全局作用域中,可能会导致很多冲突。如下图:
在这里插入图片描述
因为time函数在全局作用域中,自己又在全局范围内声明了一个time对象,导致函数名与对象名重名,造成错误


为此产生了命名空间的概念:需要使用namespace关键字,后面接命名空间的名字,然后接一堆{}即可,{}中为命名空间的成员。

#include<iostream>
using namespace std;//编译不通过
int time = 1;
int main()
{cout << "hello world!" << endl;cout << time << endl;return 0;
}	
//错误C2365“time” : 重定义;以前的定义是“函数”	#include<iostream>
using namespace std;
//声明一个命名空间xty,使自己声明的变量在xty作用域,和全局作用域分开,因此
//使用时需要在前面加一个xty::表明去xty命名空间去找
namespace xty 
{int time = 1;
};
int main()
{cout << xty::time << endl;return 0;
}
// 可以运行//输出为1

编译运行时,如果遇到未知的符号,没有特别的声明时,默认会先到全局作用域去寻找,因此在使用时,尽量声明它所在的作用域。

命名空间如何定义

#include<iostream>
using namespace std;//声明一个命名空间xty1
namespace xty
{int time = 1;void PRINT(){cout << "xty" << endl;}
};//命名空间可以嵌套定义
namespace ljj
{int time = 2;namespace ljj_PRINT {void PRINT(){cout << "ljj" << endl;}};
};//同一个工程中允许存在多个相同的命名空间,比如a.cpp,b.cpp,a.h,b.h都又相同的命名空间xty
//编译器在链接时,会将它们自动合并成一个命名空间xtyint main()
{// 命名空间的使用//正常使用cout << xty::time << endl;xty::PRINT();//嵌套使用cout << ljj::time << endl;ljj::ljj_PRINT::PRINT();return 0;
}

在这里插入图片描述

命名空间如何使用

例子按上面代码来探究:

  • 加命名空间名称+作用域限定符::
int main ()
{//正常使用cout << xty::time << endl;xty::PRINT();return 0;
}
  • 使用using将命名空间的某个成员引入
// 仅仅将xty中的time成员引入,这样使用time就不用加域作用限定符了
using xty::time
int main ()
{//正常使用cout << time << endl;xty::PRINT();return 0;
}
  • 使用using namespace 命名空间名称 引入
// 将整个命名空间展开
//这样就可以不加域作用限定符使用xty下的所用成员
using namespace xty;
int main ()
{//正常使用cout << time << endl;	PRINT();return 0;
}

先在全局作用域中去寻找,如果没有再在展开的命名空间中去寻找
如下例子:

#include<iostream>
using namespace std;//声明一个命名空间xty
namespace xty
{int time = 2;void PRINT(){cout << "xty" << endl;}
};
// 展开声明的命名空间
using namespace xty;int main()
{cout << ::time << endl;return 0;
}// 结果为time函数的地址

因为C++规定所有库函数都需要写在std的命名空间里面,using namespace std 仅仅是把std展开,只是一个命令,仍然需要把库函数头文件引进来!!保证有这个文件了,再将这个文件的std展开,就可以了。

2.缺省参数

缺省参数就是在声明或定义函数时,为函数的参数指定一个缺省值,当调用函数时,如果没有指定该参数,则默认使用缺省值。

#include<iostream>
using namespace std;//设置缺省值为10
void Text(int a = 10)
{cout << a << endl;
}
int main()
{Text();//输出结果为10Text(20);//输出结果为20return 0;
}

2.1 缺省参数分类

2.1.1全缺省参数

所有参数都缺省

#include<iostream>
using namespace std;//全缺省参数
void Text(int a = 10, int b = 20, int c = 30)
{cout << a << endl;cout << b << endl;cout << c << endl;
}
int main()
{Text();//输出结果为10,20,30//参数必须从左往右依次给出Text(1, 2);//输出结果 1,2 ,30Text(5);//输出结果5,20,30//必须从左往右给,传参错误!Text(, 0);return 0;
}

2.1.2 半缺省参数

只有部分参数缺省

#include<iostream>
using namespace std;//半缺省参数
//缺省参数必须是从右往左连续的!
void Text(int a, int b = 2, int c = 3)
{cout << a << endl;cout << b << endl;cout << c << endl;
}
int main()
{Text(10);//输出结果为10,2,3return 0;
}

注意:1.缺省参数不能在函数声明和定义同时出现;同时出现时,声明说了算,要将定义处缺省删去。2.缺省值必须是常量或者是全局变量

3. 函数重载

C++允许在同一个作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数或类型或类型顺序)不同,常用来处理实现功能类似但数据不同任务。

#include<iostream>
using namespace std;// 函数重载
int Add(int x, int y)
{return x + y;
}//1. 参数类型不同
double Add(double x, double y)
{return x + y;
}//2.参数数量不同
int Add(int x, int y, int z)
{return x + y + z;
}// 3.参数类型顺序不同
int Add(int x, double y, int z)
{return x + y + z;
}
int Add(int x, int y, double z)
{return x + y + z;
}int main()
{//感觉调用了一个函数一样,但是传进去了不同的数据类型//完成了不同的加法任务Add(1, 2);Add(1.11, 2.22);Add(1, 2, 3.33);return 0;
}

key:只有参数类型或参数顺序或参数个数不同才构成重载,返回值不同不构成重载!!!

4. 引用

引用就是给一个变量取别名。

使用符号&
类型& 引用变量名(对象名)= 引用实体
引用的类型必须和引用实体的类型相同

int main()
{int x = 10;//引用必须在声明的时候初始化,即声明的时候就得指出要给谁起别名。// int& y;int& y = x;cout << x << endl;cout << y << endl;//结果都是10return 0;
}

引用特性:1.引用一旦声明就必须初始化;2.一个变量可以有多个引用(一个变量可以有多个别名);3.引用一旦引用一个实体,就不能在引用其他实体

4.1 常引用

void Text()
{const int a = 1;//编译会出错,a本身为常量,因为区别名后权限放大//int& ra = a;const int& ra = a;//因为b为常量,编译的时候同样会出错//int& b = 10;const int& b = 10;  //正确写法double c = 1.11;//该语句编译时会出错,类型不同//int& rc = c;const int& rc = c;  //涉及到整型提升的问题,临时变量是右值,不可修改,因此const修饰能过int ii = 1;double dd = ii;//涉及到整型提成,dd实际存储的是临时变量,临时变量具有常性//double& rdd = ii;  //编译不过,因为临时变量具有常性,放大权限了const double& rdd = ii;//可以编过。}

强制类型转换、整型提升,产生的都是临时变量,临时变量具有常性;如果在原地改变,有可能空间不够!!!

4.2 引用使用场景

4.2.1 做参数

//引用传值
void Swap(int& a, int& b)
{int tem = a;a = b;b = tem;
}//传指针
void Swap(int* a, int* b)
{int tem = *a;*a = *b;*b = tem;
}
//对比发现,传引用比传指针更方便,代码量更少,更安全

4.2.2 做返回值

// 引用做返回值
//直接返回别名,可以直接改
int& Func()
{static int n = 0;//...n++;return n;
}//传指返回
int Func()
{int n = 0;//...n++;return n;
}

函数返回时,会把当前函数的栈帧清空,返回值存储到一个临时的寄存器中,用来返回给调用者;如果不存到寄存器中,栈帧清空后,才返回到调用者函数,会导致原函数的数据先被OS清空,这样会丢失数据。因此,必须有一个能存储临时变量的寄存器。
使用引用作返回值的前提是:函数返回时,返回的变量不能还给操作系统,否则不能用引用返回,必须使用传值返回

4.2.3 传值和传引用效率分析

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

下面是一组代码用来测试传值和传引用的效率比较:

#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();//cout << "hello world!" << endl;return 0;
}

下面一组代码用来测试返回值和返回引用的效率比较:

#include <time.h>
struct A{ int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a;}
// 引用返回
A& TestFunc2(){ return a;}
void TestReturnByRefOrValue()
{// 以值作为函数的返回值类型size_t begin1 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc1();size_t end1 = clock();// 以引用作为函数的返回值类型size_t begin2 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc2();size_t end2 = clock();// 计算两个函数运算完成之后的时间cout << "TestFunc1 time:" << end1 - begin1 << endl;cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

4.3 引用和指针的区别

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

	int a = 10;int* pa = &a;*pa = 20;int& ra = a;ra = 20;cout << "&a = " << &a << endl;cout << "pa = " << pa << endl;cout << "&ra = " << &ra << endl;//输出的结果都是一个地址

然而他们的底层逻辑是一样的,我们看一下汇编代码:
在这里插入图片描述
可以发现,指针和引用的汇编代码完全一样,并没有什么不同,因此我们可以推断,引用就是使用指针实现的。

4.4 指针和引用总结

  • 引用在概念上定义一个变量的别名,指针存储一个变量的地址。
  • 引用在定义时初始化(必须指出引用了谁),指针没有要求。
  • 引用在引用一个实体之后,不能再次改变,指针可以随意指向任意一个实体。
  • 没有NULL引用,但可以有NULL指针。
  • sizeof中,引用的结果始终是引用类型的大小,但指针始终是地址的字节数
  • 引用自加即实体+1,指针自加地址偏移一个类型的大小。
  • 有多级指针,但是没有多级引用。指针更复杂
  • 访问实体方式不同:指针需要显式的解引用,引用需要编译器自己处理。
  • 引用比指针使用起来更相对安全。

5. 内联函数

内联函数是用inline关键字修饰的函数,编译时C++会在调用内联函数的地方展开,这样就没有函数调用建立栈帧的开销,可以提升程序的运行效率。用来替代C语言宏的语法

5.1 特性

  1. inline是一种以空间换时间的做法,如果编译器将函数当作内联函数处理,在编译阶段会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。(长函数与递归不适合内联)
  2. inline对于编译器只是一个建议,不同编译器关于inline实现的机制可能不同,一般建议:将函数规模小(函数不是很长)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline不作展开。
  3. inline不建议声明和定义分离,分离会导致链接错误。因为inline在声明处展开,没有函数地址 ,链接就会找不到。因此,声明和定义写一块。

5.2 面试题

  1. 宏的优缺点?答:优点:增强代码复用性;提高性能。缺点:不方便调试宏(宏在编译阶段替换掉了);导致代码可维护性差,可读性差,容易误用;无类型安全检查。
  2. C++有哪些技术替代宏?答:常量定义,换用const,enum;缩小函数定义换用内联函数。

6.auto关键字

使用auto关键字可以自动推到变量的类型,不需要再次写类型。

#include<iostream>
using namespace std;int main()
{int a = 10;char b = 'x';auto c = a;auto d = b;//auto 自动推测处a, b的类型,并存储它cout << typeid(c).name() << endl;  // int cout << typeid(d).name() << endl;  //  char
}

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

6.1 auto使用方法

  1. auto与引用和指针可以合用
	int x = 10;auto a = &x;  //类型是 int*auto* b = &x; //只是强调等号右边一定填指针,b的类型也是int*auto& c = x;  //给x取别名,c的类型引用

2.在同一行定义多个变量

	auto a = 1, b = 2;  //声明并定义,能编过auto c = 1, d = 2.2; //不能编过

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

6.2 auto不能使用的场景

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

//此时会编译失败,auto不能用作形参类型
//因为编译器无法对a的实际类型进行推导
void test(auto a)
{;
}

2.auto不能直接用来声明数组

	// 声明数组int a[] = { 1, 2, 3 };  auto b[] = { 1, 2, 3 }; //编译不过

3.auto在实际中最常见的优势用法就是新式for循环,还有lambda表达式等进行配合使用。

7.基于范围for的循环表达式

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号”:”分为两部分: 第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

for(auto 迭代变量 :被迭代的范围 )

使用范例:

	int a[] = { 1,2,3,4,5 };for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++){cout << a[i] << " ";  //  1 2 3 4 5}//使用auto推到的范围for//将a中的数据依次赋给datafor (auto data : a){cout << data << " "; // 1 2 3 4 5}//使用auto& 可以修改a中的数据//给a的数据取别名for(auto& data : a){data++;    //此时a中的数据也被改变}//使用指针不能编译!!!for(auto data : &a){(*data)++;   }

7.1 范围for使用方法

1.for循环迭代的范围必须是确定的!

void Test(int arr[])
{for (auto& data : arr)   //不能编译,因为传进来的arr是地址,编译器不能分辨出这个是数组{cout<<data<<endl;}
}

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

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

相关文章

ubuntu18修改源

1. 查看当前系统的源 系统的源 2. 将sources.list备份&#xff0c;sources-bak.list是备份文件 3. 选择要换的源 # 默认注释了源码镜像以提高 apt update 速度&#xff0c;如有需要可自行取消注释 deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted un…

kettle源码远程debug调试

一、kettle启动时指定debug端口号 windows下&#xff0c;修改bat执行文件&#xff0c;同理 linux修改sh执行文件 在java执行参数的末尾添加debug参数 address为debug端口 -Xdebug -Xnoagent -Djava.compilerNONE -Xrunjdwp:transportdt_socket,servery,suspendn,address9080然…

6 应用层-6.1【实验】【计算机网络】

6 应用层-6.1【实验】【计算机网络】 前言推荐6 应用层6.1 Web服务与FTP服务配置0 搭建拓扑图1 Web演示2 FTP演示6.1.1实验章节测验 最后 前言 2023-6-25 14:35:53 以下内容源自《创作模板三》 仅供学习交流使用 推荐 4端到端协议-4.3【实验】【计算机网络】 6 应用层 6.…

前端Vue自定义发送短信验证码弹框popup 实现剩余秒数计数 重发短信验证码

前端Vue自定义发送短信验证码弹框popup 实现剩余秒数计数 重发短信验证码&#xff0c; 请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13207 效果图如下&#xff1a; 实现代码如下: # cc-codeDialog #### 使用方法 使用方法 <!-- show:是…

爬虫框架和库有多重要?

爬虫框架和库在网络数据提取和分析中非常重它们为开发人员提供了工具和功能&#xff0c;使他们能够更轻松地从互联网上抓取数据。爬虫框架和库通常提供了高效的网络请求、数据解析和存储机制&#xff0c;简化了爬取过程。 使用爬虫框架库有以下几个重要优势&#xff1a; 快速开…

Web服务器群集:Nginx网页及安全优化

目录 一、理论 1.Nginx网页优化 2.Nginx安全优化 3.Nginx日志分割 二、实验 1.网页压缩 2.网页缓存 3.连接超时设置 4.并发设置 5.隐藏版本信息 6.脚本实现每月1号进行日志分割 7.防盗链 三、总结 一、理论 1.Nginx网页优化 &#xff08;1&#xff09;概述 在企…

C++不知算法系列之计数排序算法的计数之巧

1. 前言 计数排序是较简单的排序算法&#xff0c;其基本思想是利用数组索引号有序的原理。 如对如下的原始数组中的数据(元素)排序&#xff1a; //原始数组 int nums[5]{9,1,7,6,8};使用计数排序的基本思路如下&#xff1a; 创建一个排序数组。数组的大小由原始数组的最大值…

vue3 element-plus 暗黑模式(主题切换)简易版

暗黑模式是说明 暗黑模式是指在应用程序或操作系统中使用暗色背景和浅色文本的界面设计。与传统的亮色模式相比&#xff0c;暗黑模式具有以下特点&#xff1a; 减少眼部疲劳&#xff1a;使用暗色背景可以减少屏幕发出的蓝光&#xff0c;减轻长时间使用电子设备对眼睛的疲劳程度…

python机器学习—— 数据预处理 算法初步

目录 数据预处理1.获取数据2.处理缺失值3.划分数据集4.数据预处理和PCA降维5.算法实现&#xff1a;估计器 数据预处理 1.获取数据 from sklearn.datasets import load_iris liload_iris() print("获取特征值") print(li.data) print("目标值",li.target)#…

Spring Boot 统一功能处理

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录 ⽤户登录权限效验Spring Boot 拦截器自定义拦截器将自定义拦截器加入到系统配置 拦截器实现原理 统一异常处理创建一个异常处…

LLM - 搭建 ProteinGPT 结合蛋白质结构 PDB 知识的行业 ChatGPT 系统

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/131403263 论文&#xff1a;ProteinChat: Towards Enabling ChatGPT-Like Capabilities on Protein 3D Structures 工程&#xff1a;ht…

数据库监控与调优【十七】—— 表结构设计优化

表结构设计优化 第一范式&#xff08;1NF&#xff09; 字段具有原子性&#xff0c;即数据库的每一个字段都是不可分割的原子数据项&#xff0c;不能是集合、数组、记录等非原子数据项 当实体中的某个属性有多个值时&#xff0c;必须拆分为不同的属性 例子&#xff1a; 如图…