命名空间/缺省参数/函数重载/引用等c++入门干货

本文介绍了C++的关键字区别,命名空间的作用与使用方法,解决命名空间冲突,以及C++中的引用、内联函数、auto关键字和范围for的特性。还涵盖了输入输出、缺省参数、函数重载和指针空值的概念。
摘要由CSDN通过智能技术生成

c++关键字

C语言有32个关键字,c++有63个关键字,下图是c++的部分关键字。
在这里插入图片描述

命名空间

namespace发明缘由

函数,变量和类的名字都存在全局作用域中,很有可能导致冲突和名字污染。

#include<stdlib>
#include<stdio.h>
int rand=10;
int main()
{
	printf("%d",rand);
}

运行之后报错
在这里插入图片描述
C语言无法解决这个问题,于是c++就提出命名空间namespace。

命名空间定义

定义命名空间时,在namespace关键字后面跟着的是命名空间的名字,后面再接一对{ },{ }里面就是命名空间的成员(包括函数和变量)。

1.命名空间可以定义函数,变量,类型
#include<iostream>
using namespace std;
namespace solution
{
	int rand = 10;
}

int main()
{
	cout << solution::rand << endl;
}
2.命名空间可以嵌套
#include<iostream>
using namespace std;
namespace solution
{
	int rand = 10;
	namespace lyb
	{
		int Add(int x, int y)
		{
			return x + y;
		}
	}
}

int main()
{
	cout << solution::rand << endl;
	cout << solution::lyb::Add(1, 2) << endl;
}
允许存在相同名字的命名空间,编译器最后会合到一个命名空间中,如果是在不同的文件中的命名空间最后也会合并。

命名空间的使用

声明:由于rand和std里的rand有冲突,接下来都只展示Add函数。

1.加入命名空间名称和域作用限定符
#include<iostream>
using namespace std;
namespace solution
{
	int rand = 10;
}

int main()
{
	cout << solution::rand << endl;
}
2.用using将命名空间里某个成员引入
#include<iostream>
using namespace std;
namespace solution
{
	int rand = 10;
	namespace lyb
	{
		int Add(int x, int y)
		{
			return x + y;
		}
	}
}
using solution::lyb::Add;
int main()
{
	cout << solution::rand << endl;
	cout << Add(1, 2) << endl;
}
使用using namespace引入
#include<iostream>
using namespace std;
namespace solution
{
	int rand = 10;
	namespace lyb
	{
		int Add(int x, int y)
		{
			return x + y;
		}
	}
}
using namespace solution::lyb;
int main()
{
	cout << solution::rand << endl;
	cout << Add(1, 2) << endl;
}

输入/输出

1.使用标准输入输出的时候必须包含头文件以及std命名空间。
2.<<是流插入运算符,>>是流提取操作符。
3.无需像printf一样手动控制输出对象类型,会自行识别。
4.cout和cin分别是istream和osteam类型的对象,>>和<<属于运算符重载。

#include<iostream>
using namespace std;
int main()
{
	int a, b;
	cin >> a >> b;
	cout << a << " " << b << endl;
}

缺省参数

缺省参数的概念

缺省参数是声明和定义函数时为函数的参数指定的一个缺省值,在调用函数时,如果没有传参就调用缺省值,若传参则缺省值不用。

#include<iostream>
using namespace std;
void func(int n = 0)
{
	cout << n << endl;
}
int main()
{
	func();
	func(1);
	return 0;
}

在这里插入图片描述

缺省参数分类

1.全缺省参数
顾名思义,就是所有参数都有缺省值。

void func(int a=0,int b=1,int c=10)
{
	cout<<a<<b<<c<<endl;
}

2.半缺省参数
不是所有的参数都缺省。
注意:半缺省参数只能从右往左给,不能间隔着给,也不能在声明和定义中同时出现,缺省值必须为常量或者全局变量,C语言不支持缺省参数。

void func(int a,int b=10,int c=0)
{
	cout<<a<<b<<c<<endl;
}

函数重载

概念

c++允许在一个域里面出现功能类似的同名函数,这些同名函数的形参列表不同(参数个数/参数类型/参数类型顺序不同)
1.参数类型不同

#include<iostream>
using namespace std;

int Add(int x, int y)
{
	return x + y;
}
double Add(double x, double y)
{
	return x + y;
}

int main()
{
	Add(1.0,1.0);
	Add(1,1);
	return 0;
}

2.参数个数不同

#include<iostream>
using namespace std;

int Add(int x, int y)
{
	return x + y;
}
int Add(int x, int y,int z)
{
	return x + y + z;
}

int main()
{
	Add(1,2,3);
	Add(1,2);
	return 0;
}

3.参数类型顺序不同

#include<iostream>
using namespace std;

int Add(char x, int y)
{
	return x + y;
}
int Add(int x, char y)
{
	return x + y;
}

int main()
{
	Add('a', 1);
	Add(1, 'a');
	return 0;
}

原理

函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。
在vs下可以转到反汇编。
在这里插入图片描述
可以看到两个Add函数后面括号的地址是不一样的,每个编译器都有自己的函数名修饰规则。
注意:如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办
法区分。

引用

定义

引用相当于给一个变量取了一个别名,新变量和原先变量共用同一个内存空间。

void func()
{
    int a = 10;
    int& ra = a;
    printf("%p\n", &a);
    printf("%p\n", &ra);
}

若你运行了上诉代码,你会发现上述代码输出的地址是一模一样的。

引用特性

1.引用必须初始化
2.一个实体可以有多个引用
3.引用一旦引用一个实体,就不能引用其他实体
4.关于常引用,要注意不能权限放大,const类型的变量和常量在这方面是类似的。

void func()
{
    const int a = 10;
    //int& ra = a;   // 该语句编译时会出错,a为常量,是一种权限放大
    const int& ra = a;
    // int& b = 10; // 该语句编译时会出错,b为常量,也是一种权限放大
    const int& b = 10;
    double d = 12.34;
    //int& rd = d; // 该语句编译时会出错,类型不同
    const int& rd = d;
}

应用场景

1.做参数
2.做返回值

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    //Add(3, 4);//把这个代码屏蔽掉,ret输出的值可能是正确的,但如果没有屏蔽掉,那么ret就是一个随机值
    cout <<  ret <<endl;
    return 0}

上述代码其实是有问题的,因为已经出了Add函数栈帧,c变量以及c值返回时候产生的临时变量都已经销毁,其实是已经无法访问了,只是在vs下没有报错,但如果你再多运行一个函数,把Add使用过的空间占用了,你会发现输出的ret是个随机值,如上述代码。

传值和传引用的效率比较

若是传值,函数不会直接传递实参或者让变量本身直接返回,而是传递一份临时拷贝,效率其实是低下的,特别是考虑到参数和返回值类型非常大时,效率就很低。

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

在这里插入图片描述
我们可以看见这个效率差别是很大的。

引用和指针的区别

引用的底层逻辑其实就是指针,用引用只是为了便于理解。
1.引用必须初始化,而指针没有这个要求。
2.引用不能变换实体,而指针可以随时改变存储另一个同类型的地址。
3.没有空引用,但有空指针。
4.在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节大小。
5.引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。
6.有多级指针,但是没有多级引用(比如说引用的引用还是同一个实体)。
7.访问实体方式不同,指针需要显式解引用,引用编译器自己处理。

内联函数

作用

编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,能够提高程序的效率。

特性

1.编译阶段,会用函数体替换函数调用。
2.使用inline只是对编译器的一个建议,如果选择内联的函数太大,编译器会选择忽略这个指令。
3.用在非递归,规模小,调用频繁的函数上。
4.不能声明和定义分离在不同文件里,因为inline函数已经展开,编译器在链接的时候是找不到地址的,就会链接错误。

auto关键字

产生缘由

1.变量类型复杂
2.容易拼写错误
3.typedef不能解决所有问题

typedef char* pstring;
#include<iostream>
using namespace std;
typedef char* pstring;
int main()
{
	const pstring p1;
	const pstring* p2;
	return 0;
}

在这里插入图片描述
因为typedef不是简单的 宏替换,p1报错是因为前面的变量类型不是被替换成const char而是char const。
注:使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto
的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译时会将auto替换为变量实际的类型。

规则

1.auto与auto是没有区别的,但使用auto时右边必须是一个指针。

int main()
{
	int x = 10;
	auto a = &x;
	auto* b = &x;
	auto& c = x;
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	*a = 20;
	*b = 30;
	c = 40;
	return 0;
}

在这里插入图片描述
2.在同一行声明多个变量时,这多个变量必须是同一种类型,否则会报错,因为auto只对第一个变量进行推导,然后用推导出来的类型定义其他变量。
3.auto不能声明函数参数,也不能直接用来声明数组。

范围for

用法:for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

int arr={1,2,4,5,7,8};
for(auto x:arr);

使用条件:
1.for循环的范围必须是确定的,对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin()和end()的方法,begin()和end()就是for循环迭代的范围。
以下代码就有问题

void func(int array[])
{
    for(auto& e : array)
        cout<< e <<endl;
}

2.迭代对象要实现++和==的操作。

指针空值

在c++中,NULL被定义为0,只有nullptr才是空指针。

  • 19
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值