目录
命名空间
内联函数
概述
特性:
命名空间
在C/C++中,变量,函数和和类这些名称都存在于全局作用域中,可能会导致很多冲突,使用命名空间的目的是对标识符的名称进行本地化,避免命名冲突或名字污染,namespace关键字就是解决这种问题的。如下程序并无问题:
#include<stdio.h>
int rand=0;
int main(){printf("%d\n",rand);return 0;
}
但是如果在上述代码中加入一段#include<stdlib.h>,因为stdlib.h头文件里面有一个rand的函数,此时就会出现如下错误:
命名冲突(C语言中没有方法可以解决这个问题):
1.我们写的代码跟库冲突
2.我们互相之间写的代码冲突
这时C++中就有了namespace用来定义一个命名空间,语法:namespace 命名空间名{成员},示例如下:
#include<stdio.h>
#include<stdlib.h>
namespace ThisLocality{int rand = 0;
}int main() {printf("%d\n", rand);
}
这时就解决命名冲突这个问题,命名空间就像一堵墙把rand围起来了,此时的rand默认访问的是全局就是stdlib.h头文件里面的函数rand,而namespace里的rand就不会被访问了,既然是访问函数rand那么就用%p来打印地址,此时代码运行如下:
此时想访问namespace里面rand,只需要在rand前面加,命名空间名::(域作用限定符)变量名就可以了,示例代码如下:
#include<stdio.h>
#include<stdlib.h>
namespace ThisLocality{int rand = 0;
}
int main() {printf("%p\n", rand);//访问的是stdlib.h头文件里面的函数printf("%d\n", ThisLocality::rand);//此时访问的是bit命名空间里的rand变量
}
此外,命名空间除了可以定义变量也可以定义函数,结构体,还可以嵌套,代码如下:
namespace ThisLocality{//定义变量int rand=10;//定义函数int Add(int left,int right){return left+right;}//定义结构体struct Node{struct Node*next;int val;};//嵌套namespace ThisLocality2{int rand=100;}}
上述几种定义的访问方式如下图:
上述中函数和变量的访问方式只不过就是在名字后面加了()里面放对应的参数,而定义命名空间中的结构体的时候命名空间名是加在结构体名前面的所以是ThisLocality::Node 结构体变量名,最后访问int rand=10;首先要找到命名空间ThisLocality然后再::命名空间名,这是找命名空间里的命名空间最后就到了这个命名空间里了,最后::变量名就可以访问了,我们知道嵌套如果太深也不好一般来说两层基本就够了。
下面在Stack.h中定义了ThisLocality然后在Stack.cpp中完成的ThisLocality里面函数的内容,因为他们是同名所以会自动合并成一个命名空间,所以在Test.cpp中导入Stack.h就可以使用ThisLocality中的函数了。
Stack.cpp中的代码:
#include"Stack.h"
namespace ThisLocality{void StackInit(ST*ps){ps->a=NULL;ps->top=0;ps->capacity=0;}void StackPush(ST*ps,int x){//...}
}
Stack.h中的代码:
#pragma once
#include<stdio.h>
namespace ThisLocality {typedef struct Stack {int* a;int top;int capacity;}ST;void StackInit(ST* ps);void StackPush(ST*ps, int x);
}
Test.cpp中的代码:
#include<stdio.h>
#include<stdlib.h>
#include"Stack.h"
int main() {ThisLocality::ST s;Thislocality::StackInit(&s);Thislocality::StackPush(&s,1);Thislocality::StackPush(&s,2);Thislocality::StackPush(&s,3);Thislocality::StackPush(&s,4);}
展开命名空间,使用展开命名空间就像把命名空间里面的代码直接放到展开位置上了,使用了展开命名空间之后就不需要再使用,命名空间::这段了可以直接使用里面的变量或者函数跟普通变量使用方法并无区别。
using namespace 命名空间名;
展开示例:
可以看到展开之后不需要加,命名空间名::,如其中的ST它会先去全局中找没找到就会去声明的命名空间里面找,如果在这两个地方都没有找到就会报错,但是不建议大量使用这种方法。
我们经常会在很多C++的代码中看到:using namespace std;这段代码,这段代码其实是C++官方库里面的命名空间,这里是直接把这个库展开了,这样就可以随便用里面的东西了。C++把东西放在里面,就是因为容易发生冲突,如果展开那不是本末倒置了,但是日常小程序为了方便可以这样做。
内联函数
概述
以inline修饰的函数叫做内联函数,编译时 C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数是提升程序运行的效率。
如有一个相加两数的函数:
int ADD(int a,int b){return a+b;
}
如果在上述函数前面添加inline关键字将其改成内联函数,在编译期间会替换调用函数体的函数的调用位置。这类似于C语言中的宏的替换:
#include<iostream>
using namespace std;
#define ADD(x,y) ((x)+(y))
#define ADD1(x,y) ((x)+(y));
int main() {cout << ADD(23, 87);//上述代码中的ADD替换成 宏中的内容 //cout<<((23)+(87));//宏后面不能加;是因为宏是完全替换的操作如下就会出错cout << ADD1(23, 24) << endl;//因为其替换之后代码为://cout<<((23)+(24));<<endl; 所以一般不建议在宏后加;
}
宏优点:直接替换调用位置的代码,不用建立函数栈帧,提高效率
宏的缺点:
容易出错,语法细节要求多
而且宏不能调试(在预处理阶段就替换了)
还没有类型的检查(如我传一个int和char会导致结果不理想)
C++中就用:enum const inline来替代宏
enum和const是宏常量
inline是宏函数
而inline却跟正常函数一样,可以调式,有类型检查
使用inline函数的代码如下:
inline int ADD(int a,int b)
{int c=x+y;return c;
}
int main(){int ret1=ADD(23,2);//不用建立函数栈帧return 0;
}
从上可以看到其语法跟普通函数没有不同只是在函数前面加上了inline。
在release模式下,可以查看编译器生成的汇编代码中是否存在call Add ,而在debug模式下,需要对编译器进行设置,否则不会展开(替换)。
特性:
由上可知inline是一种以空间换时间的做法,如函数被编译器当做内联函数则会在编译阶段,会用函数体替换函数调用,这可能会导致文件变大,但是少了调用开销可以提高程序运行效率。
inline对于编译器来说只是建议,在不同的编译器中inline的实现也有所不同。
inline修饰:函数不是很大(取决于对应的编译器),不是递归,也不频繁调用的函数可以使用inline修饰,不然编译器会忽略inline的特性。
使用inline时并不建议将声明和定义分开,因为这样会导致链接错误,因为inline被展开,然后就没有了函数地址,链接就会找不到。