【C++】C++入门小知识

目录

  1. 命名空间
  2. 缺省参数
  3. 函数重载
  4. 引用
  5. 内联函数
  6. nullptr

1、命名空间

命名空间的出现是为了解决C语言中出现的命名冲突问题,关键字namespace

include<stdio.h>
include<stdlib.h>
int rand=0;
int main()
{
	printf("%d",rand);
	return 0;
}
//程序运行会报错:rand变量和头文件<stdlib.h>中的rand()函数的冲突问题

用namespace关键字解决上面的问题

include<stdio.h>
include<stdlib.h>
namespace name
{
	int rand=0;
}
int main()
{
	printf("%d",name::rand);
	return 0;
}

1.1、域作用限定符 ::
域包括:全局域、局部域、命名空间域、类域等
域作用限定符的使用-> 命名空间域的名称 :: 该命名空间内的变量名 ----- 到该命名空间域中访问该变量
例子:

#include<stdio.h>

int x = 0;
namespace example
{
	int x = 1;
}
int main()
{
	int x = 2;
	printf("%d\n", x);//访问的局部变量
	printf("%d\n", ::x);//访问的全局变量
	printf("%d\n", example::x);//访问的命名空间example中的变量
	return 0;
}

小结:
编译器的搜索原则

  • 不指定域,先局部再全局
  • 指定域,直接到指定域搜索

1.2、命名空间的定义
namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员

//命名空间域里面可以定义变量、结构体、函数等等
#include<iostream>
namespace name1
{
	int x = 0;
	int Add(int a, int b)
	{
		return a + b;
	}
}
namespace name2
{
	struct Node
	{
		int val;
		struct Node* next;
	};
}
int main()
{
	printf("%d\n", name1::Add(1, 2));
	printf("%d\n", name1::x);
	struct name2::Node list;//使用name2域中的结构体声明变量list  
	return 0;
}

1.3、命名空间的使用
①使用域作用限定符

int main()
{
	printf("%d",N::a);//访问N域里面的变量a
	return 0;
}

②使用using将命名空间中某个成员引入

using N::b;
int main()
{
	printf("%d",b);//访问N域里面的变量b
	return 0;
}

③使用using namespace 命名空间名称 引入

using namespace N;//直接将N里面的所有內容展开到全局域中,可以随意访问
int main()
{
	printf("%d",b);//访问N域里面的变量b
	return 0;
}

2、缺省参数

2.1、 缺省参数概念:就是不给形参赋值时,会有默认值
void Func(int a=0)
{
	cout<<a<<endl;
}
int main()
{
	Func();//打印0
	Func(10);//打印10
	return 0;
}

2.2、缺省参数分类
①全缺省

//全缺省:所以参数均有缺省值
void func(int a=0,int b=1,int c=2)
{
	cout<<a<<endl;
	cout<<b<<endl;
	cout<<c<<endl;
}
int main()
{
	//全缺省在给参数时,不能跳跃着给,要按顺序
	func();
	func(10);
	func(10,20);
	func(2,3,4);
	return 0;
}

②半缺省

//半缺省的缺省值要从右往左连续给
void func(int a,int b,int c=2)
{		//左       <-       右
	cout<<a<<endl;
	cout<<b<<endl;
	cout<<c<<endl;
}
int main()
{
	func(1,2);
	func(2,3,4);
	return 0;
}

注意:

  • 缺省值只能是常量或者全局变量
  • 缺省参数不能在函数定义和声明中同时出现
  • 缺省函数在声明和定义分离的情况下,缺省参数在函数声明中给

3、函数重载

3.1、函数重载概念:在同一作用域内,定义了函数名相同,但参数类型不同、参数个数不同、参数类型顺序不同的同名函数,构成重载函数

int add(int x,int y)
{
	return x+y;
}
//参数类型不同
double add(double x,double y)
{
	return x+y;
}
//参数个数不同
int add(int x,int y,int z)
{
	return x+y+z;
}
//参数类型顺序不同
int add(double x,int y)
{
	return x+y;
}

4、引用

4.1、引用概念:引用就是给已经存在的变量取一个别名,编译器不会给别名单独开辟内存空间,它和它的引用对象共用同一块内存空间

比如:李逵,在家叫铁牛,在江湖上叫黑旋风,不论是铁牛还是黑旋风,他们指的都是李逵

//类型& 引用变量名=引用实体;
int main()
{
	int a=10;
	int& b=a;//定义引用类型
	cout<<a<<endl;//10
	cout<<b<<endl;//10
	printf("%p",&a);
	printf("%p",&b);
	//两个变量的地址是一样的
	return 0;
}

注意:引用类型和引用实体类型要是同种类型(或者是可以隐式转换为引用类型的对象)
4.2、引用特性:
①引用在定义时必须初始化
②一个变量可以有多个引用
③引用一旦引用一个实体,就不可以再引用其他实体

int main()
{
	int a=10;
	//int& r;不初始化编译器会报错
	int& ra=a;
	int& raa=a;
	return 0;
}

4.3、常引用

int main()
{
	const int a=10;
	//int& r=a;a是常量,不能这样引用,会报错,这样引用相当于扩大权限,本来a不能改的,现在可以通过变量r来改变,这样显然是不合理的
	const int& ra=a;//权限要平移才可以,这样引用常量就没问题
	const int&b=10;//直接引用常量10也是同理

	double c=13.14;
	//int& rc=c;这句会报错,因为编译器不允许非const引用绑定到不同类型的对象上,引用类型和引用实体类型要是同种类型(或者是可以隐式转换为引用类型的对象),double不能隐式转化为int类型,并且会丢失精度,所以会报错
	const int& ret=c;
	//这里不会报错是因为C++允许const引用绑定到临时对象上,即使这些临时对象类型和引用对象类型不完全匹配,这种情况下,编译器会创建一个临时的int对象,该对象通过double类型的值c转化为int类型得到的,const引用ret引用的就是这个临时对象
	return 0;
}

总结:

  • const引用可以绑定到临时对象上,因为编译器知道你不会通过这个引用来修改它所引用的值
  • 绑定过程中会进行类型转换,这里是double到int的转换,但只有当引用是const时,这种转换才是允许的。
  • 尽管引用ret在语法上看起来像是直接引用变量c,但实际上它是引用了一个临时的int值。每次通过ret访问值时,都会重新进行这个转换

4.4、引用的使用场景
①做参数
②做返回值

int& add(int x, int y)
{
	int c = x + y;
	return c;
}
int main()
{
	int& ret = add(1, 2);
	add(2, 3);
	cout << ret << endl;
	return 0;
}
//这里的ret的结果是5,add函数运行结束后,该函数对应的栈空间就会被回收,局部变量c也就没有意义了,ret实际上就是引用了一块已经被释放的空间,再次调用add函数时,ret引用的c位置就被修改成了5,因此ret的值也改变了

注意:

  • 函数返回时,如果返回对象是临时变量(出了函数作用域不在的)必须用传值返回,反之,用引用返回
  • 以值作为参数或者返回值类型时,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低

4.5、引用和指针的区别
引用在语法概念上没有独立的空间,就是一个别名,和引用对象共用一块空间,但是在底层是有空间的,因为引用是按照指针的方式来实现的

不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量的地址
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能在引用其他实体,而指针可以随时指向同类型的实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中的含义不同,引用结果为引用类型的大小,但指针始终都是地址空间所占的字节数(32位平台:4个字节,64位平台:8个字节)
  6. 引用自加为引用实体加1,指针自加表示向后偏移一个类型的大小

5、内联函数

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

在这里插入图片描述
如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用

在这里插入图片描述
加了关键字inline,在编译阶段直接完成函数体内的操作,不需要建立栈帧

5.2、inline的特性

  1. inline是一种以空间换时间的做法,如果编译器将函数当作内联函数来处理,在编译阶段,会用函数体替换函数调用,这样可能会导致目标文件变大,但少了调用开销,提高了运行效率
  2. inline不建议声明和定义分离,分离会导致链接错误,因为inline被展开了,就没有函数地址了,链接就会找不到
  3. 适合函数规模较小的函数,不是递归且频繁调用的函数采用inline修饰,否则编译器会忽略掉inline的特性

内联只是向编译器发出请求,编译器可以忽略

6、nullptr

NULL其实是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:
#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

可以看到NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量,显而易见,这样会出现一些分歧

void fun(int x){}
void fun(int* p){}
int main()
{
	fun(0);
	fun(NULL);
	fun((int*)0);
	return 0;
}
//fun(NULL)本身是想去调用传指针的函数,但是他会被理解为字面量0调用func(int)类型的函数,这和他想调用的完全不一样,只能去进行强制类型转换,才可以调用fun(int* p)

C++为了解决这一缺陷,衍生出了关键字nullptr,nullptr仅表示指针空指,在后续完全可以使用nullptr代替NULL避免出现不必要的问题,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同

  • 31
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值