当前位置: 首页 > news >正文

c++入门——基础知识点(1)

1、命名空间

在c/c++中,变量、函数和类都是大量存在的,这些变量、函数和类的名称都将存于全局作用域中,可能会导致许多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染namespace关键字的出现就是针对这种问题的。
(1)命名空间的定义
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。

// 1、普通的命名空间
namespace N1 //namespace是关键字 N1是命名空间的名称
{
 	int a;
 	// 命名空间中的内容,既可以定义变量,也可以定义函数
 	int Add(int left,int right)
 	{
 		return left + right;
	 }
} 
//2、命名空间可以嵌套
namespace N2
{
	int a;
	int b;
	int Add(int left,int right)
	{
		return left + right;
	}
	namespace N3
	{
		int c;
		int d;
		int sub(int left,int right)
		{
			return left - right;
		}
	}
}
//3、同一个工程中允许存在多个相同名称的命名空间,但是编译后会合成同一个命名空间
namespace N3
{
	int mul(int left,int right)
	{
		return left * right;
	}
}

(2)命名空间的使用
基本上使用方式就是三种:

  • 命名空间名称 + 作用域限定符
  • 使用using将命名空间中成员引入
  • 使用using namespace将命名空间引入
#incldue<iostream>
//定义一个命名空间
namespace N
{
	int a= 10;
	int b = 20;
	
	int Add(int left,int right)
	{
		return left + right;
	}
	
	int sub(int left,int right)
	{
		return left - right;
	}
}
using N::b;//第二种方法,使用using将命名空间中成员引入,利用using将命名空间N中的变量b引入主函数作用域
/*int main()
{
    //printf("%d\n",a);//编译时报错,a没有在主函数作用域当中
	//printf("%d\n",N::a);//第一种方法,命名空间名称 + 作用域限定符 输出10
    //printf("%d\n",b);//输出20
	return 0;
}
*/
using namespace N;//第三种方法,使用using namespace将命名空间引入.
int main()
{  
     printf("%d\n",a);//输出10
     printf("%d\n",b);//输出20
     printf("%d\n",Add(50,40);//输出90
     printf("%d\n",snb(50,10);//输出40
}

注意: 一个命名空间就定义了一个新的作用域,命名空间中所有的内容都局限于该命名空间中。
建议: 单个小程序用这三种方法都可以,但是建议用第三种方便一次到位(视情况而定),而在大的工程中建议不要使用第三种,在一些大的项目中很容易造成和其他的名字冲突,同时容易引起错误,小项目你可能感觉不出来,但这种习惯其实不好,而且也就失去了名字空间本身的意义了。

2、缺省参数

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

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

int main()
{
	TestFunc();//没有传参数时,输出默认的0 
	TestFunc(10)//有参数时,输出参数10 
	return 0;
}

(2)缺省参数分为:全缺省参数、半缺省参数

//全缺省参数
void Func_1(int a = 10,int b = 20,int c = 30)
{
	cout<<"a="<<a<<endl;
	cout<<"b="<<b<<endl;
	cout<<"c="<<c<<endl;
}
//半缺省参数
void Func_2(int a,int b = 10,int c = 20)
{
	cout<<"a="<<a<<endl;
	cout<<"b="<<b<<endl;
	cout<<"c="<<c<<endl;
}

int main()
{
	//全缺省参数
	Func(); //无参数,则输出默认的值a = 10, b = 20, c = 30
	//半缺省参数
	Func(); //编译错误,因为半缺省必须把那个参数在函数中给出
	return 0;
}

注意:

  1. 半缺省参数必须从右往左依次来给出,不能间隔着给
  2. 缺省参数不能在函数声明和定义中同时出现
  3. 缺省值必须是常量或者是全局变量
  4. c语言不支持(编译器不支持)

3、函数重载

(1)函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。
c++环境下:
在这里插入图片描述
c语言环境下:
在这里插入图片描述
从图片中可以看出三个同名函数在不同语言环境编译中结果不同。因为这C语言不支持函数重载而c++却支持函数重载。而C语言报错是:命名冲突。这就引出来一个问题:为什么C语言不支持重载而c++支持重载?
问题: 为什么C语言不支持重载而c++支持重载?
答: 因为c++支持Name MangLing函数名修饰规则/变名机制,

void f1(int i,double d)
{
}
void f1(double d, int i)
{
}
void f(int* p,int i)
{
}
int main()
{
	return 0;
}

看下汇编代码(全文都是在Linux下面做的实验,Windows类似,你也可以参考《一道简单的题目引发的思考》一文,那里既用到Linux下面的反汇编和Windows下面的反汇编,并注明了Linux和Windows汇编语言的区别)。我们执行命令
在这里插入图片描述
我们可以发现编译之后,重载函数的名字变了不再都是f1!这样不存在命名冲突的问题了,但又有新的问题了——变名机制是怎样的,即如何将一个重载函数的签名映射到一个新的标识?我的第一反应是:函数名+参数列表,因为函数重载取决于参数的类型、个数,而跟返回类型无关。重载函数的签名映射到一个新的标识:返回类型+函数名+参数列表
c++详细函数重载

4、名字修饰(name Mangling)

在C/C++中,程序运行必须经历下来几个阶段:预处理、编译、汇编、链接
在这里插入图片描述
Name Mangling是一种在编译过程中,将函数、变量的名称重新改编的机制,简单来说就是编译器为了区分各个函数,将函数通过某种算法,重新修饰为一个全局唯一的名称。
在这里插入图片描述
从上面可以看出来C语言的名字修饰规则非常简单,只是在函数名前面添加了下划线。报错的原因:Add函数只给了声明没有给定义,因此在链接时就会报错,提示:在main函数中引用的Add函数找不到函数体。从报错结果中可以看到,C语言只是简单的在函数名前添加下划线。因此当工程中存在相同函数名的函数时,就会产生冲突。
在这里插入图片描述
通过上述错误可以看出,编译器实际在底层使用的不是Add名字,而是被重新修饰过的一个比较复杂的名字,被重新修饰后的名字中包含了:函数的名字以及参数类型。这就是为什么函数重载中几个同名函数要求其参数列表不同的原因。只要参数列表不同,编译器在编译时通过对函数名字进行重新修饰,将参数类型包含在最终的名字中,就可保证名字在底层的全局唯一性。

5、extern “C”

有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。
在这里插入图片描述
从报错来看,编译器把Add函数按照C语言的风格来编译的。从_Add就可以看出。

相关文章:

  • c/c++内存管理
  • 函数模板、类模板初识
  • 【Linux】进程地址空间了解
  • 【Linux】入门基础命令(1)
  • c++入门——基础知识点(2)
  • 【Linux】基础文件的I/O操作(1)
  • 【Linux】进程信号
  • 【Linux】网络编程套接字(1)
  • 【Linux】UDP网络套接字编程
  • 【数据结构:树】——搜索二叉树-K模型(非递归和递归)
  • 【C++】——STL关联式容器认识以及使用
  • TCP三次握手和四次挥手详解
  • 【Linux】进程控制
  • 【Linux】进程程序替换——exec函数簇
  • 【Linux】入门基础命令(2)
  • Javascript设计模式学习之Observer(观察者)模式
  • LeetCode18.四数之和 JavaScript
  • Map集合、散列表、红黑树介绍
  • Nacos系列:Nacos的Java SDK使用
  • Node项目之评分系统(二)- 数据库设计
  • SpiderData 2019年2月16日 DApp数据排行榜
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • V4L2视频输入框架概述
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 如何解决微信端直接跳WAP端
  • 如何选择开源的机器学习框架?
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 温故知新之javascript面向对象
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • ​Python 3 新特性:类型注解
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • #stm32整理(一)flash读写
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • (003)SlickEdit Unity的补全
  • (1)bark-ml
  • (day 12)JavaScript学习笔记(数组3)
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (二)PySpark3:SparkSQL编程
  • (篇九)MySQL常用内置函数
  • (四) 虚拟摄像头vivi体验
  • (一)UDP基本编程步骤
  • (转)编辑寄语:因为爱心,所以美丽
  • *** 2003
  • .net core开源商城系统源码,支持可视化布局小程序
  • .Net FrameWork总结
  • .net refrector
  • .NET6使用MiniExcel根据数据源横向导出头部标题及数据
  • .net分布式压力测试工具(Beetle.DT)
  • .NET牛人应该知道些什么(2):中级.NET开发人员
  • .net中的Queue和Stack
  • .考试倒计时43天!来提分啦!
  • [ IOS ] iOS-控制器View的创建和生命周期
  • [].slice.call()将类数组转化为真正的数组
  • [AIGC] Java 和 Kotlin 的区别