c++入门——基础知识点(1)
1、命名空间
在c/c++中,变量、函数和类都是大量存在的,这些变量、函数和类的名称都将存于全局作用域中,可能会导致许多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染namespace关键字的出现就是针对这种问题的。
(1)命名空间的定义
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
// 1、普通的命名空间
namespace N1 //namespace是关键字 N1是命名空间的名称
{
int a;
// 命名空间中的内容,既可以定义变量,也可以定义函数
int Add(int left,int right)
{
return left + right;
}
}
//2、命名空间可以嵌套
namespace N2
{
int a;
int b;
int Add(int left,int right)
{
return left + right;
}
namespace N3
{
int c;
int d;
int sub(int left,int right)
{
return left - right;
}
}
}
//3、同一个工程中允许存在多个相同名称的命名空间,但是编译后会合成同一个命名空间
namespace N3
{
int mul(int left,int right)
{
return left * right;
}
}
(2)命名空间的使用
基本上使用方式就是三种:
- 命名空间名称 + 作用域限定符
- 使用using将命名空间中成员引入
- 使用using namespace将命名空间引入
#incldue<iostream>
//定义一个命名空间
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;
}
}
using N::b;//第二种方法,使用using将命名空间中成员引入,利用using将命名空间N中的变量b引入主函数作用域
/*int main()
{
//printf("%d\n",a);//编译时报错,a没有在主函数作用域当中
//printf("%d\n",N::a);//第一种方法,命名空间名称 + 作用域限定符 输出10
//printf("%d\n",b);//输出20
return 0;
}
*/
using namespace N;//第三种方法,使用using namespace将命名空间引入.
int main()
{
printf("%d\n",a);//输出10
printf("%d\n",b);//输出20
printf("%d\n",Add(50,40);//输出90
printf("%d\n",snb(50,10);//输出40
}
注意: 一个命名空间就定义了一个新的作用域,命名空间中所有的内容都局限于该命名空间中。
建议: 单个小程序用这三种方法都可以,但是建议用第三种方便一次到位(视情况而定),而在大的工程中建议不要使用第三种,在一些大的项目中很容易造成和其他的名字冲突,同时容易引起错误,小项目你可能感觉不出来,但这种习惯其实不好,而且也就失去了名字空间本身的意义了。
2、缺省参数
(1)缺省参数:缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。
void TestFunc(int a = 0)
{
cout<<a<<endl;
}
int main()
{
TestFunc();//没有传参数时,输出默认的0
TestFunc(10)//有参数时,输出参数10
return 0;
}
(2)缺省参数分为:全缺省参数、半缺省参数
//全缺省参数
void Func_1(int a = 10,int b = 20,int c = 30)
{
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<"c="<<c<<endl;
}
//半缺省参数
void Func_2(int a,int b = 10,int c = 20)
{
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<"c="<<c<<endl;
}
int main()
{
//全缺省参数
Func(); //无参数,则输出默认的值a = 10, b = 20, c = 30
//半缺省参数
Func(); //编译错误,因为半缺省必须把那个参数在函数中给出
return 0;
}
注意:
- 半缺省参数必须从右往左依次来给出,不能间隔着给
- 缺省参数不能在函数声明和定义中同时出现
- 缺省值必须是常量或者是全局变量
- c语言不支持(编译器不支持)
3、函数重载
(1)函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。
c++环境下:
c语言环境下:
从图片中可以看出三个同名函数在不同语言环境编译中结果不同。因为这C语言不支持函数重载而c++却支持函数重载。而C语言报错是:命名冲突。这就引出来一个问题:为什么C语言不支持重载而c++支持重载?
问题: 为什么C语言不支持重载而c++支持重载?
答: 因为c++支持Name MangLing函数名修饰规则/变名机制,
void f1(int i,double d)
{
}
void f1(double d, int i)
{
}
void f(int* p,int i)
{
}
int main()
{
return 0;
}
看下汇编代码(全文都是在Linux下面做的实验,Windows类似,你也可以参考《一道简单的题目引发的思考》一文,那里既用到Linux下面的反汇编和Windows下面的反汇编,并注明了Linux和Windows汇编语言的区别)。我们执行命令
我们可以发现编译之后,重载函数的名字变了不再都是f1!这样不存在命名冲突的问题了,但又有新的问题了——变名机制是怎样的,即如何将一个重载函数的签名映射到一个新的标识?我的第一反应是:函数名+参数列表,因为函数重载取决于参数的类型、个数,而跟返回类型无关。重载函数的签名映射到一个新的标识:返回类型+函数名+参数列表。
c++详细函数重载
4、名字修饰(name Mangling)
在C/C++中,程序运行必须经历下来几个阶段:预处理、编译、汇编、链接。
Name Mangling是一种在编译过程中,将函数、变量的名称重新改编的机制,简单来说就是编译器为了区分各个函数,将函数通过某种算法,重新修饰为一个全局唯一的名称。
从上面可以看出来C语言的名字修饰规则非常简单,只是在函数名前面添加了下划线。报错的原因:Add函数只给了声明没有给定义,因此在链接时就会报错,提示:在main函数中引用的Add函数找不到函数体。从报错结果中可以看到,C语言只是简单的在函数名前添加下划线。因此当工程中存在相同函数名的函数时,就会产生冲突。
通过上述错误可以看出,编译器实际在底层使用的不是Add名字,而是被重新修饰过的一个比较复杂的名字,被重新修饰后的名字中包含了:函数的名字以及参数类型。这就是为什么函数重载中几个同名函数要求其参数列表不同的原因。只要参数列表不同,编译器在编译时通过对函数名字进行重新修饰,将参数类型包含在最终的名字中,就可保证名字在底层的全局唯一性。
5、extern “C”
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。
从报错来看,编译器把Add函数按照C语言的风格来编译的。从_Add就可以看出。