目录
2.同一工程可以有多个名称相同的命名空间,编译器会将其合成为一个命名空间
一、命名空间
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字 污染,namespace关键字的出现就是针对这种问题的。
1、定义
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
// 普通的命名空间
namespace N1 // N1为命名空间的名称
{
// 命名空间中的内容,既可以定义变量,也可以定义函数
int a;
int Add(int left, int right)
{
return left + right;
}
}
2、性质
1.命名空间可以嵌套
namespace N1
{
int a;
int b;
int Add(int left, int right)
{
return left + right;
}
namespace N2
{
int c;
int d;
int Sub(int left, int right)
{
return left - right;
}
}
}
2.同一工程可以有多个名称相同的命名空间,编译器会将其合成为一个命名空间
namespace N1
{
int Mul(int left, int right)
{
return left * right;
}
}
namespace N1
{
int a;
int Add(int left, int right)
{
return left + right;
}
}
注:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
3、使用
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;
}
}
int main()
{
printf("%d\n", a); // 该语句编译出错,无法识别a
return 0;
}
上例中的a编译器无法识别,这是由于我们将a限定在了N这个作用域内,如若想要使用N中类似于a的元素,有如下三种方式:
1.加命名空间名称及作用域限定符
这种做法的好处是对于不常用到的命名空间,可以减少进程开销。
int main()
{
printf("%d\n", N::a);
return 0;
}
2.using引入空间成员
这种做法的局限性是必须保证该项目中不能有其他与该空间成员重名的元素,也就是该方式仅能用于各个引入的成员都是独立且没有歧义的情况下
using N::b;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
return 0;
}
3.using namespace 引入命名空间
using namespce N;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
Add(10, 20);
return 0;
}
对于频繁使用到的命名空间可以用这种方法去编写,例如最常用的C++标准库命名空间std。
二、引用
1、概念
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
正如:图片里的小鸡,一些人可能把他叫苏珊,也有一些人管他叫咯咯。
引用的一般形式为:类型& 引用变量名(对象名) = 引用实体
void TestRef()
{
int a = 10;
int& ra = a;//<====定义引用类型
printf("%p\n", &a);
printf("%p\n", &ra);
}
下图1中的b,我们是将a的值赋给了它,而重载则是以类似于指针的方式,使b成为了a的别名。
2、特性
1.引用在定义时必须初始化
void main() {
int a ; //a没有初始化
int &b = a; //a没有实体,别名b没有任何意义
}
引用不可以存在空引用的情况
2.一个变量可以有多个引用
void main() {
int a = 10;
int &b = a;
int &c = a;
int &d = a;
}
3.引用一旦引用一个实体,再不能引用其他实体
void main() {
int a = 10;
int c = 20;
int &b = a;
int &b = c; //相当于重定义了
}
❤对于引用我们可以通过函数传参来理解
void Add(int a, int b, int& retRest) {
retRest = a + b;
}
void main() {
int a = 10;
int b = 20;
int res;
Add(a, b, res);
}
通过监视窗口可以看出,对于形参a,b只是传值到函数里,对于传进来的值开辟了一个新的空间来存值,而对于res的引用并没有另辟空间,直接使用函数外res的地址。
3、使用场景
1.做参数
void Swap(int &left, int &right) {
int temp = left;
left = right;
right = temp; }
2.做返回值
int& Add(int a, int b) {
int sum = a + b;
return sum;
}
void main() {
int sum=0;
int a = 10;
int b = 20;
int res = Add(a, b);
}
函数的引用不能乱用,如果函数返回时,给出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。
通过观察不难发现,对于参数引用和函数引用是两种不同的写法,虽然其作用是相同的,&靠在参数一边时目的是提醒这里引用的是这个参数,而靠近函数类型时则是想说明引用这个函数的返回值。
4、效率对比
1.传值与传引用
#include<iostream>
using namespace std;
#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void main()
{
A a;
// 以值作为函数参数
size_t begin1 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc1(a);
size_t end1 = clock();
// 以引用作为函数参数
size_t begin2 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc2(a);
size_t end2 = clock();
// 分别计算两个函数运行结束后的时间
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
2.传值与传引用的返回值
#include<iostream>
using namespace std;
#include <time.h>
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void main()
{
// 以值作为函数的返回值类型
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;
}
5、引用方式
1.引用变量
void main() {
int a = 10;
int& b = a;
}
2.引用数组
void main() {
int ar[10] = { 1,2,3,4,5 };
int(&br)[10] = ar;
}
3.引用指针
void main() {
int a = 10;
int& b = a;
int* p = &a;
int* &pr = p;
}
4.常引用
void main() {
int a = 10;
const int &b = a; //常引用
}
加上const后,不能通过b修改a的空间
void main() {
const int a = 10; //a为常量此时语法要求b必须也为常量
const int &b = a;
}
在C程序中,将a的值传给b,中间需要经历一个隐式转换过程,该过程会形成一个无名的临时变量用于储存a赋给b的部分的值,该无名临时变量为常量,因此要想用b去引用a,则要在b前加const
void main() {
double a = 12.34;
const int& b = a;
}
6、引用和指针的区别
以下知识由于有些地方还没有学习了解过,我并未完全理解,仅罗列过来日后再补充验证
在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
int main()
{
int a = 10;
int& ra = a;
cout<<"&a = "<<&a<<endl;
cout<<"&ra = "<<&ra<<endl;
return 0; }
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
引用和指针的不同点:
1. 引用在定义时必须初始化,指针没有要求
2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型
实体
3. 没有NULL引用,但有NULL指针
4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占
4个字节)
5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
6. 有多级指针,但是没有多级引用
7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
8. 引用比指针使用起来相对更安全
给个三连支持一下俺吧😊