一,命名空间(namespace)
1.1命名空间的作用与定义
我们在学习c的过程中,经常会碰到命名冲突的情况。就拿我们在c语言中的一个string函数来说吧:
cpp
int strncat = 0;
int main()
{
printf("%d", strncat);
return 0;
}
当我们运行之后,毫无疑问的会报错:
有人可能会说,这是库函数,我不用这定义不久行了吗?但我们日后的工作之中,经常会有好几个人来共同完成一个项目,所以难免有人写的函数名称会与他人相冲突,这时后就需要使用我们的namespace来解决。
在介绍namespace的用法前,我们先来了解下它的定义:
1.定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
2.namespace本质是定义出⼀个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不在冲突了。
3.C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。
Tips:namespace只能定义在全局,当然他还可以嵌套定义。项目工程中多文件中定义的同名namespace会认为是⼀个namespace,不会冲突。
1.2命名空间的使用
1.2.1命名空间的使用方法
由于命名空间的本质是一个作用域,所以我们通常以->命名空间名称:: 所需使用对象 的方式来使用命名空间中的内容,::为域作用限定符,比如我们以上面的例子为例:
cpp
namespace ikun
{
int strncat = 0;
}
int main()
{
printf("%d", ikun::strncat);
return 0;
}
这时再运行程序就不会报错, 但需要注意的是命名空间可以被视为全局作用域的扩展,它们为标识符引入了一个新的层次结构。这意味着在全局命名空间之外,你可以有多个命名空间,每个命名空间都有自己的标识符集。
Tips:C++的标准库都放在一个叫std(standard)的命名空间中。
1.2.2常见的几种使用情况
**1.**不同文件中的相同名称命名空间,在运行时会被看作一个空间进行使用,也就是说二者之间的内容会进行合并。
**2.**如果在使用时对空间中的部分或全部内容频繁使用而又不想麻烦,可以使用:
cpp
using ikun::strncat;//部分成员的展开
using namespace ikun;//空间所有成员的展开
**3.**如果不想麻烦的去定义多个命名空间,则可以嵌套定义:
cpp
namespace ikun
{
namespace A {
void func() {
//
}
}
namespace B {
void func() {
//
}
}
}
此时对单个成员的使用方式即为ikun::A::func()。多次嵌套使用方式一致。
二,C++的输入输出
在C语言中,我们使用的输入函数为scanf,输出函数为printf。而在C++中,我们使用的则是C++标准库中的std::cin,std::cout来进行输入输出。
我们来用下面这个例子来区别二者的输入输出方式:
cpp
#include <iostream>//这⾥我们没有包含<stdio.h>,也可以使⽤printf和scanf,在包含<iostream>间接含
//vs系列编译器是这样的,其他编译器可能会报错。
int main()
{
int a = 0;
std::cin >> a ;//>>符号为流插入符号,<<为流输出符号
printf("%d\n", a);
scanf("%d", &a);
std::cout << a << std::endl;//std::endl为C++中的换行方式
return 0;
}
输入与输出结果如下
可见C与C++之间并不冲突的同时,我们也发现了C++不需要指定输入或输出的数据类型(本质是通过函数的重载实现的),但这里我们不详细介绍IO流,因为IO流涉及类和对象,运算符重载、继承等很多面向对象的知识,这些知识我们还没有讲解,所以这里我们只能简单认识⼀下C++ IO流的用法,后面我们会有专门的一篇文章来介绍IO流库。
三,缺省参数
缺省函数,通俗的来讲就好像一个备胎,我们先以一个例子来引出:
cpp
int add(int a = 10, int b = 0)
{
return a + b;
}
int main()
{
int a = add();
std::cout << a << std::endl;
}
当我们一个参数都不给时,他的输出结果就是10,下面我们来详细介绍缺省函数的概念与使用方法:
3.1全缺省
全缺省就是全部形参给缺省值,与我们上面的缺省方式一样,这即为全缺省。
3.2半缺省
半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。
cpp
int add(int a,int b = 0);//半缺省的正确写法
int add(int a = 0,int b);//错误写法
3.3 给实参的方式
正如我们上面缺省的方式一样,我们在调用函数给实参时应该从左向右给实参,而不能跳着给,也不能从左往右给。
cpp
int add(int a, int b = 0,int c = 10)
{
return a + b + c;
}
int main()
{
int b = add(1);//正确方式
int c = add(, , 10);//错误方式
int a = add(1,,10);//错误方式
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
}
3.4声明与定义的缺参方式
当我们的函数在只有定义但没有函数的声明时,可以直接在函数的定义部分缺省。但如果我们的函数在头文件中有声明时,则只能在声明部分缺省:
cpp
int add(int a, int b = 0, int c = 10);
int add(int a, int b,int c)
{
return a + b + c;
}
//正确方式
int add(int a, int b, int c);
int add(int a, int b = 0, int c = 10)
{
return a + b + c;
}
//编译报错
四,函数重载
C++支持在同⼀作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调⽤就表现出了多态行为,使用更灵活。C语言是不支持同⼀作用域中出现同名函数的。比如如下例子:
cpp
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void swap(double* a, double* b)
{
double tmp = *a;
*a = *b;
*b = tmp;
}
再比如这样:
cpp
void swap(int* a, double* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void swap(double* a, int* b)
{
double tmp = *a;
*a = *b;
*b = tmp;
}
或者是这样:
cpp
void swap(int* a)
{
//
}
void swap(int* a, double* b)
{
//
}
也就是说,对于函数的参数位置的参数个数,参数位置,参数类型不同,但名称相同,它们则都是重载函数。但需要注意,返回值不能作为是否为重载函数的条件,因为调用时也无法区分它们:
cpp
int swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
return tmp;
}
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
还有当出现如下的类似情况时,虽然二者符合函数重载的定义,但当我们使用f()进行调用时,会产生歧义,导致编译报错:
cpp
void f1()
{
cout << "f()" << endl;
}
void f1(int a = 10)
{
cout << "f(int a)" << endl;
}