目录
[(3)使用using namespace 命名空间名称引入(最好不要全部授权);](#(3)使用using namespace 命名空间名称引入(最好不要全部授权);)
一、命名空间;
1.如何定义;
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{} 中即为命名空间的成员。命名空间中可以定义变量/函数/类型;命名空间可以嵌套;同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。目的是为了解决命名冲突(新建的变量名与库的变量重名);
代码举例:
定义一个名称为bin的命名空间:
cpp
namespace bin//命名空间可以定义变量函数函数结构体。
{
int rand1 = 0;
int val = 10;
int add(int x, int y)
{
return x + y;
}
struct a
{
struct a* next;
int y;
};
}
嵌套定义命名空间:
cpp
namespace bin//命名空间可以定义变量函数函数结构体。
{
int rand1 = 0;
int val = 10;
int add(int x, int y)
{
return x + y;
}
struct a
{
struct a* next;
int y;
};
namespace hhh
{
int rand = 1;
int val1 = 15;
}
}
2.如何使用;
(1)使用加命名空间名称及作用域限定符;
代码举例:
cpp
namespace bin//命名空间可以定义变量函数函数结构体。
{
int rand1 = 0;
int val = 10;
int add(int x, int y)
{
return x + y;
}
struct a
{
struct a* next;
int y;
};
namespace hhh
{
int rand = 1;
int val1 = 15;
}
}
int main()
{
printf("%d\n",bin::rand1);//域作用限定符
printf("%d\n", bin::add(1, 2));
struct bin::a x;//引用结构体的时候bin::放在struct后面。
x.next = NULL;
x.y = 10;
return 0;
}
运行结果:
(2)使用using将命名空间中某个成员引入;
代码举例:
cpp
namespace bin//命名空间可以定义变量函数函数结构体。
{
int rand1 = 0;
int val = 10;
int add(int x, int y)
{
return x + y;
}
struct a
{
struct a* next;
int y;
};
namespace hhh
{
int rand = 1;
int val1 = 15;
}
}
using bin::add;
using bin::rand1;
using bin::a;
int main()
{
printf("%d\n", bin::hhh::rand);//::域作用限定符
printf("%d\n", rand1);
printf("%d\n", add(1, 2));
a x;
x.next = NULL;
x.y = 10;
printf("%d %p", x.y, x.next);
return 0;
}
运行结果:
(3)使用using namespace 命名空间名称引入(最好不要全部授权);
代码举例:
cpp
namespace bin//命名空间可以定义变量函数函数结构体。
{
int rand1 = 0;
int val = 10;
int add(int x, int y)
{
return x + y;
}
struct a
{
struct a* next;
int y;
};
namespace hhh
{
int rand = 1;
int val1 = 15;
}
}
using namespace bin;
int main()
{
printf("%d\n", bin::hhh::rand);//::域作用限定符
printf("%d\n", rand1);
printf("%d\n", add(1, 2));
a x;//引用结构体的时候bin::放在struct后面。
x.next = NULL;
x.y = 10;
printf("%d %p", x.y, x.next);
return 0;
}
运行结果:
(4)命名空间合并;
命名空间可以自动合并; 多个文件里相同名字的命名空间可以自动合并;(同名的变量会出错,不写或者再写一层命名空间,写在新建的命名空间里)。
3.输入输出函数;
使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间使用方法使用std。cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,与"\n"类似,他们都包含在包含<iostream >头文件中。<<是流插入运算符,>>是流提取运算符。
代码举例:
cpp
#include<iostream>//把头文件拷贝过来,std在头文件里的命名空间里。
using namespace std;
namespace jdg
{
int ruler;
int sheer;
}
int main()
{
int x = 0;
int y = 0;
cin >> x >>y;
cout << x << y << endl;
cout << "top is " << jdg::ruler << endl;
cout << "jug is" << jdg::sheer << endl;
return 0;
}
运行结果:
二、缺省函数;
1.函数定义;
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实 参则采用该形参的缺省值,否则使用指定的实参。
代码举例:
cpp
#include<iostream>
using namespace std;
void func(int a = 1)
{
cout << a << endl;
}
int main()
{
func(2);//有传参就用传的参;
func();
return 0;
}
运行结果:
2.分类;
(1)全缺省函数;
全部参数都有缺省值;
代码举例:
cpp
void func1(int a = 1, int b = 2,int c=3)
{
cout << "a=="<< a << endl;
cout << "b==" << b << endl;
cout << "c==" << c << endl;
}
int main()
{
func1();
func1(3);///从左往右依次传参;
func1(3,1);
func1(4, 5, 6);
return 0;
}
运行结果:
(2)半缺省函数;
部分参数有缺省值;参数必须传至少一个值,缺省值必须从左往右给;
代码举例:
cpp
#include<iostream>
using namespace std;
void func(int a, int b = 1, int c = 1)//半缺省;必须传一个,缺省值必须从左往右给;
{
cout << a + b + c << endl;//传参数默认传给第一个;
}
int main()
{
/*fun();*/
/*fun(10);*/
func(1);
func(1, 2);
func(1, 2,3);
return 0;
}
运行结果:
(3)缺省函数声明与定义;
缺省参数不能在声明和定义同时给,只能在声明中给;因为如果在声明的地方给定义,多个文件包含头文件会造成重复定义,有可能出错;
三、函数重载;
C语言不允许同名函数,C++允许同名函数,但要求构成重载;(这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同);
1.重载类型分类;
(1)类型顺序不同;
代码举例:
cpp
void func(int a, double b)
{
cout << "void func(int a, double b)" << endl;
}
void func(double a, int b)//函数的地址是函数第一句指令的地址,只声明不定义的函数没有地址;
{
cout << "void func(double a, int b)" << endl;
}
int main()
{
func(1, 1.2);//call func(加函数地址);//符号表中存的是函数名加函数名修饰规则生成的代码(把类型带进名字)
//C语言中存的是函数名, 区分不开
func(1.2, 1);
return 0;
}
运行结果:
(2)参数个数不同;
代码举例:
cpp
void func(int a, double b)
{
cout << "void func(int a, double b)" << endl;
}
void func(double a, int b,int c)//函数的地址是函数第一句指令的地址,只声明不定义的函数没有地址;
{
cout << "void func(double a, int b,int c)" << endl;
}
int main()
{
func(1, 1.2);//call func(加函数地址);//符号表中存的是函数名加函数名修饰规则生成的代码(把类型带进名字)
//C语言中存的是函数名, 区分不开
func(1.2, 1,2);
return 0;
}
运行结果:
(3)参数类型不同;
代码举例:
cpp
void func(int a, int b)
{
cout << "void func(int a, int b)" << endl;
}
void func(double a, int b)//函数的地址是函数第一句指令的地址,只声明不定义的函数没有地址;
{
cout << "void func(double a, int b)" << endl;
}
int main()
{
func(1, 1.2);//call func(加函数地址);//符号表中存的是函数名加函数名修饰规则生成的代码(把类型带进名字)
//C语言中存的是函数名, 区分不开
func(1.2, 1);
return 0;
}
运行结果:
2.函数重载原因;
C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
代码举例:
运行结果:
由图所示,同名函数call的地址不一样,因为C++把参数也编进了函数地址,所以参数不同的同名函数地址不同。
四、引用;
1.简要介绍;
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
代码举例:
cpp
int main()
{
int a = 10;
int& b = a;//b是a的引用,b是a的别名; &在类型和名的中间才是引用
cout << a<<endl;
cout << b << endl;
cout << &a << endl;//a、b取的是一个变量
cout << &b << endl;
cout << ++a << endl;
cout << ++b << endl;//引用可以替代传地址再解引用的情况;
return 0;
}
运行结果:
结果分析:
b是a的别名,b改变a改变,a改变,b也改变;
2.引用的应用;
(1)传引用返回:
代码举例:
cpp
int& count()//返回n的引用,
{
int n = 0;
n++;
return n;//临时
}
int main()
{
int ret = count();//这里打印的结果可能是1,也可能是随机值(取决于空间释放后是否置成随机值)
cout << ret << endl;
cout << ret << endl;
return 0;
}
运行结果:
结果分析:
这里打印的结果可能是1,也可能是随机值;count返回的是count函数栈帧的一块空间,这部分的值取决于count栈帧结束后是否置为随机值;
(2)传引用传参:
代码举例:
cpp
void swap(int& a, int& b)
{
int tem = a;
a = b;
b = tem;
}
int main()
{
int x = 1, y = 0;
swap(x, y);
cout << x << " "<<y << endl;//引用可以减少拷贝;
return 0;
}
运行结果:
结果分析:
a是x的别名,b是y的别名;a、b的改变会影响x、y的值;
3.引用的限制条件;
1.必须再定义的地方初始化;
2.一个变量可以有多个引用;a的别名可以有 b、c、d,b的别名e,c的别名f;
3.引用一旦,就不能当别的变量的引用了,不能改变指向;
如果出了函数,对象还在就可以使用引用返回;否则不能使用引用返回;
传引用传参(任何时候):1.提高效率2.输出线参数(形参改变影响实参);
传引用返回(出了函数作用域对象还在):1.提高效率2.修改返回对象(例如returnsize);
4.权限问题;
简要介绍:
引用时发生类型转换 提升 截断 临时拷贝都会产生临时变量,要注意权限;(赋值不需要注意权限)。引用的权限一定要小于等于原变量的权限;
代码举例:
cpp
int main()
{
const int a = 10;//const 已经限定了;
//权限的放大
int& b = a;//这是权限的放大;引用的过程中权限可以平移,可以缩小,但是不能放大;
//权限的平移
const int& c = a;
//权限的缩小;
int x = 10;//x修改
const int& y = x;//y不能修改,但是x的修改会影响y;
//赋值不受权限的控制;
const int q = 10;
int p = q;赋值不受权限的控制;
int a = 10;
double d = a;//可以
double& c = a;//不可以
const double& q = a;//在c++里,发生类型转换赋值时候会产生临时变量,右边先赋给临时变量,临时变量再赋给左边
//权限放大 //临时变量具有常性;
return 0;
}
运行结果:
结果分析:
a是const,所以引用只能是权限平移,只能是const,b是放大,a是平移;x是int,所以引用可以是缩小、平移,不可放大;y是缩小;引用在发生类型转换的时候,会产生一个临时变量,这个临时变量是右值的临时拷贝,具有常属性,所以只能发生权限平移;c是权限放大,q是权限平移;注意,传参返回的是变量的临时拷贝,引用的时候也只能权限平移,记得加const;
五、内联函数;
1.简要介绍;
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调 用建立栈帧的开销,内联函数提升程序运行的效率。
代码举例:
cpp
inline int add(int x, int y)//调用时候不建立函数栈帧,而是把函数的内容直接替换到调用函数的位置;
{
return (x + y);
}
int main()
{
int m = add(1, 2);//在反汇编代码中没有调用函数;
cout << m << endl;
return 0;
}
运行结果:
结果分析:
由汇编代码得,函数调用得时候并没有call函数地址,即没有调用函数,而是直接把函数里得内容进行了运算;这就是inline函数,不会调用函数,而是直接展开;
2.常见问题;
代码举例:
cpp
inline int func()
{
int x1 = 0;
int x2 = 0;
int x3 = 0;
int x4 = 0;
int ret = 0;
ret += x1;
ret += x1;
ret += x1;
ret += x1;
ret -= x1;
ret -= x1;
ret -= x1;
ret -= x1;
ret *= x1;
ret *= x1;
ret *= x1;
ret *= x1;
ret /= x1+1;
ret /= x1+1;
ret /= x1+1;
ret /= x1+1;
return ret;
}
int main()
{
/*int m = add(1, 2);*/
int m = func();//很复杂就不会按照内联函数算,还是会调用函数
cout << m << endl;
return 0; //100行调用内敛函数(函数内容100行);全部展开 10000行;不展开(一行函数调用,一行call函数地址,共两百行)
运行结果:
结果分析:
内敛函数很复杂或者有递归,即使声明它是内联函数,也不会展开,而是去调用函数。
3.声明和定义;
inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。内联函数不生成指令(不生产函数地址,不进入符号表),在调用的地方展开;
4.优缺点:
内敛函数:
优点:1.没有宏的缺点;
缺点:内联函数很长会使程序变大(代码膨胀,安装包变大);因此:如果内联函数很大或者是递归,编译器会自动否认他是内联;
六、auto关键字;
1.简要介绍;
C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一 个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
代码举例:
cpp
#include<vector>;
#include<string>;
int main()
{
int a = 0;
auto b = a;//b 是整形;//(自动推导);
auto c = &a;//c 是指针;
auto& d = a;//d 是引用;
//auto普通场景没有价值,类型很长才有价值;
std::vector<std::string> v;
std::vector<std::string>::iterator it = v.begin();
auto he = v.begin();//简化代码
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;//看类型
cout << typeid(he).name() << endl;
return 0;
}
运行结果:
结果分析:
auto可以根据右边的值自动判断变量类型,简化了变量名比较长时程序得复杂度;
1. auto不能作为函数的参数。
2. auto不能直接用来声明数组。
2.范围for;
代码举例:
cpp
int main()
{
int array[] = { 1,2,3,4,5,6 };//遍历数组;
for (auto e : array)//依次取数组中的元素赋值给e,自动迭代,自动判断结束;(e改变 不影响数组的值)
{//auto可以改为int,但是最好是auto;
cout << e << " ";
}
cout << endl;
for (auto& e : array)//依次取数组中的元素赋值给e,自动迭代,自动判断结束;(e改变 不影响数组的值)
{//auto可以改为int,但是最好是auto;
e*=2;
}
for (auto e : array)//依次取数组中的元素赋值给e,自动迭代,自动判断结束;(e改变 不影响数组的值)
{//auto可以改为int,但是最好是auto;
cout << e << " ";
}
//范围for针对的是数组名,函数传参传的是指针,不可以使用;
cout << endl;
return 0;
}
运行结果:
结果分析:
依次取数组中的元素赋值给e,自动迭代,自动判断结束;(如果e不是引用,e改变 不影响数组的值);范围for针对的是数组名,函数传参传的是指针,不可以使用范围for;
七、nullptr;
1.简要介绍;
在C++11中,NULL是0,默认为整形;如果要传一个空指针,可以是(int*)NULL,也可以直接nullptr;
代码举例:
cpp
void f(int)
{
cout <<"void f(int)"<< endl;
}
void f(int*)
{
cout << "void f(int*)" << endl;
}
int main()
{
f(0);
f(NULL);//NULL直接定义成0,默认成整形;
f((int*)NULL);
f(nullptr);//nullptr 值是0,类型是int*;
return 0;
}