【C++】命名空间/重载/缺省/引用/内联函数/auto关键字

本文介绍了C++中的命名空间,如何使用namespace避免命名冲突,以及缺省参数的概念和分类。接着讨论了函数重载,展示了如何通过不同的参数列表实现相同功能的多个函数。此外,还详细解释了引用的概念,包括常引用和引用的使用场景,以及引用与指针的区别。最后,提到了内联函数的作用和限制,auto关键字的自动类型推导功能,以及C++11引入的范围for循环的便利性。

目录

1.命名空间

1.1namespace关键字

1.2命名空间的使用

2.缺省参数

2.1缺省参数的概念

2.2缺省参数的种类

3.函数重载

3.1函数重载的概念

4.引用 ‘&’

4.1引用的概念

4.2常引用

4.3引用的使用场景

4.3.1引用做参数

4.3.2引用做返回值

4.4引用和指针的区别

5.内联函数/auto/范围for

5.1内联函数

5.2auto关键字

5.3范围for


1.命名空间

1.1namespace关键字

在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存 在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化, 以避免命名冲突或名字污染,namespace关键字的出现就是解决这种问题的。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int rand = 10;
    printf("%d",rand);  //在C语言中,自定义rand与stdlib库内rand函数重名,无法正常使用
    return 0;
}

1.2命名空间的使用

为了解决以上C的问题,我们可以自定义命名空间来避免与其他相同命名造成冲突。

#include <stdio.h>
#include <stdlib.h>

namespace hehe
{
    int rand = 10;
}

using namespace hehe;//展开自定义hehe命名空间

int main()
{
    printf("%d",rand);
    printf("%d",hehe::rand);//若未展开命名空间,可添加域作用限定符
    return 0;
}

C++提供了标准函数库的命名空间std,这样在使用标准库函数时,就不用依次添加std::这样的域作用限定符了。

#include <iostream>
using namespace std;

int main()
{
    //std::cout << 1 << std::endl;//未展开std命名空间时
    cout << 1 << endl;//展开std命名空间后
    return 0;
}

在实际生产,std标准空间会给我们造成很大的不便,因此我们往往不展开std命名空间而仅仅展开其中一些较为常用的函数。

#include <iostream>
using std::cout;//只使用特定函数命名空间
using std::endl;

int main()
{
	cout << 1 << endl;
	cout << 2 << endl;
	cout << 3 << endl;
	return 0;
}

(尽管std标准命名空间在实际运用中有很大弊端,但在日常练习中,展开标准命名空间往往会方便很多)


2.缺省参数

2.1缺省参数的概念

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

void Func(int a = 0)
{
    cout<<a<<endl;
}
int main()
{
    Func();     // 没有传参时,使用参数的默认值
    //结果为0
    Func(10);   // 传参时,使用指定的实参
    //结果为10   
    return 0;
}

2.2缺省参数的种类

缺省参数分为全缺省和半缺省两大类。

全缺省:顾名思义,可以省去所有参数,因此在定义函数时要将所有变量设定初始化值。

//全缺省
void Func1(int a = 0)
{
	cout << a << endl;
}

void Func2(int a = 10, int b = 20, int c = 30)
{
	cout << "a " << a << endl;
	cout << "b " << b << endl;
	cout << "c " << c << endl;

}

半缺省:即可以省去部分参数,在定义时按照从右到左的顺序依次给出(不可间隔)

//半缺省
void Func(int a, int b = 10, int c = 20)
 {
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }

 注意:1.缺省函数不可在声明和定义中同时出现,为实用起见,最好放在声明处

            2.缺省值必须是常量或全局变量


3.函数重载

3.1函数重载的概念

在实际开发中,有时候我们需要实现几个功能类似的函数,只是有些细节不同。例如希望交换两个变量的值,这两个变量有多种类型,可以是 int、float、char、bool 等,我们需要通过参数把变量的地址传入函数内部。在C语言中,程序员往往需要分别设计出三个不同名的函数,但是在C++中,函数允许拥有多个名字,只需形参列表(参数个数类型类型顺序)不同即可。

#include <iostream>
using namespace std;

//交换 int 变量的值
void Swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

//交换 float 变量的值
void Swap(float *a, float *b)
{
    float temp = *a;
    *a = *b;
    *b = temp;
}

//交换 char 变量的值
void Swap(char *a, char *b)
{
    char temp = *a;
    *a = *b;
    *b = temp;
}

//交换 bool 变量的值
void Swap(bool *a, bool *b)
{
    char temp = *a;
    *a = *b;
    *b = temp;
}

int main()
{
    //交换 int 变量的值
    int n1 = 100, n2 = 200;
    Swap(&n1, &n2);
    cout<<n1<<", "<<n2<<endl;
   
    //交换 float 变量的值
    float f1 = 12.5, f2 = 56.93;
    Swap(&f1, &f2);
    cout<<f1<<", "<<f2<<endl;
   
    //交换 char 变量的值
    char c1 = 'A', c2 = 'B';
    Swap(&c1, &c2);
    cout<<c1<<", "<<c2<<endl;
   
    //交换 bool 变量的值
    bool b1 = false, b2 = true;
    Swap(&b1, &b2);
    cout<<b1<<", "<<b2<<endl;

    return 0;
}

4.引用 ‘&’

4.1引用的概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空 间,它和它引用的变量共用同一块内存空间。

void Test()
{
    int a = 10;
    int& ra = a;//定义引用类型
//此时ra和a的值相同,地址也相同
}

注意:1. 引用在定义时必须初始化

           2. 一个变量可以有多个引用

           3. 引用一旦引用一个实体,再不能引用其他实体

int main()
{
	int a = 0;
	int& b = a;
	int& c = b;
	int& d = a;
	cout << &a << &b << &c << &d << endl;//同一块空间
	b++;
	d++;
	cout << a << endl;//a=2
	int x = 11;
	d = x;//x赋值给d,d仍是a的引用  引用不可修改地址
	cout << a;//a=11

	return 0;
}

4.2常引用

在引用的过程中,变量权限只可平移或缩小,不可被放大。

int main()
{
	//❌引用过程中,权限不可被放大
	//const int a = 0;
	//int& b = a;

	//✔  没有放大权限  d为c的拷贝,d的改变不影响c
	const int c = 0;
	int d = c;

	//✔  引用过程中,权限可以平移或缩小
	int x = 0;
	int& y = x;
	const int& z = x;
	x++;//缩小z作为x的别名的权限,不改变x的权限
	//z++;  z作为别名权限被缩小


	//整形提升/转换 时会产生临时变量,临时变量具有常属性(const),无法被修改
	const int& m = 10;
	double dd = 1.11;
	int ii = dd;
	//int& rii = dd;  ❌ 类型转换时会产生int类型的临时变量  相当于权限放大
	const int& rii = dd;//✔


	//int& ret1 = func1();//❌  权限放大 返回临时变量  具有常性
	const int& ret1 = func1();//✔ 权限平移
	int rret1 = func1();  //拷贝

	int& ret2 = func2();//返回x的引用,权限平移
	const int& rret2 = func2();//权限缩小

	int i = 0;
	double j = 1.11;
	if (j > i)
	{
		cout << "xxxxx" << endl;
		//运行结果为xxxxx
		//运算符两侧不同时,会对两侧从小到大整形提升(用临时变量的形式)
	}

	return 0;
}

4.3引用的使用场景

4.3.1引用做参数

引用可以用做输出型参数,提高程序效率

void Swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

4.3.2引用做返回值

引用做返回值时,不生成临时变量,减少拷贝,提高效率 

int& Count()
{
   static int n = 0;//static修饰的变量存储于静态区
   n++;
   // ...
   return n;
}

注意:大部分时候都可以用引用传参,但要谨慎使用引用做返回值,若对象出了作用域被销毁,则不可用引用返回

4.4引用和指针的区别

引用和指针的不同点:

1. 引用概念上定义一个变量的别名,指针存储一个变量地址。

2. 引用在定义时必须初始化,指针没有要求

3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何 一个同类型实体

4. 没有NULL引用,但有NULL指针

5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下占4个字节)

6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

7. 有多级指针,但是没有多级引用

8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理

9. 引用比指针使用起来相对更安全

//引用和指针的区别
//没有NULL引用,有NULL指针
//引用更安全(没有空的概念,即不可使用未初始化的引用)
#include <iostream>
using namespace std;

int main()
{
	int a = 10;

	//语法上:不开辟空间,对a取别名
	//底层汇编指令实现来看,引用和指针实现类似,也会开辟空间 
	int& ra = a;
	ra = 20;

	//语法上:开辟空间,存储a的地址
	int* pa = &a;
	*pa = 30;

	return 0;
}

5.内联函数/auto/范围for

5.1内联函数

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

//宏函数  
//不需要建立栈帧 提高调用效率 复用性好
//复杂 易出错,代码可读性差,无法调试,缺少安全性检查
//#define Add(x,y) (x+y)*10

//内联函数
inline int Add(int x, int y)
{
	return(x + y) * 10;
}

int main()
{
	for (int i = 0; i < 10000; i++)
	{
		cout << Add(i, i + 1) << endl;
	}
	
	return 0;
}

内联函数使用时,声明和定义不可分离,即需要放在同一项目中。相较于宏函数,内联函数可以解决宏函数的全部问题;

内联函数适用于短小且调用频繁的函数,但是内联函数会导致可执行程序变大,代码膨胀;
inline对编译器只是建议,最总决定权归属编译器自身决定;
较长函数或递归函数函数即使加了inline也会被编译器否决。

5.2auto关键字

随着程序的不断发展,复杂化已经成为程序中不可避免的趋势,而这样就容易导致:1.拼写错误 2.类型错误。为了简便此类问题,auto关键字可以自动推导变量类型,减少低级错误。

C++11中,标准委员会赋予了auto全新的含义:auto不再是一个存储类型指示符,而是作为一 个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

int main()
{
	int a = 0;
	int b = a;
	auto c = a;//根据右侧表达式推到c的类型;
	auto d = 1 + 1.11;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;

	vector<int>v;
	vector<int>::iterator it1 = v.begin();
	auto it2 = v.end();

	std::map<std::string, std::string> dict;
	std::map<std::string, std::string>::iterator dit1 = dict.begin();
	auto dit2 = dict.end();//auto可以大幅简洁化代码

	return 0;
}

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

5.3范围for

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

void TestFor()
{
    int array[] = { 1, 2, 3, 4, 5 };
    for(auto& e : array)
         e *= 2;
    for(auto e : array)
         cout << e << " ";
    return 0;
}
//与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环

注意:使用范围for时,for循环的迭代范围必须是确定的,对于数组而言即是数组首尾元素,对于类类型而言,应该提供begin和end来确定范围。


Fight!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hehelm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值