引言
详细介绍C++中的知识点:命名空间,C++的输入输出,C++中的(C语言中没有)函数缺省参数,函数的重载。
一、命名空间
1、namespace的价值
在C/C++中,变量、函数和类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
看个案例:
#include<stdlib.h>
#include<stdio.h>
int rand = 10;
int main()
{
printf("%d \n", rand);
return 0;
}
运行这段代码,编译器会报错:
rand是一个库函数,这里又给rand定义为int型,导致rand重定义了,编译会报错。
为了解决这样的问题,C++中引入了命名空间的概念
2、namespace的定义
• 定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量 / 函数 / 类型等。
namespace test
{
int a = 10;
int Add(int a, int b)
{
return a + b;
}
int rand;
}
• namespace本质是定义出一个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不在冲突了。
在 C++ 里,:: 是作用域解析运算符,其作用是明确指定要使用的变量、函数或者类属于哪个命名空间或者类。大白话就是:使用该命名空间里面的东西。
#include<iostream>
namespace test
{
int a = 10;
int Add(int a, int b)
{
return a + b;
}
int rand = 20;
}
int main()
{
printf("%d \n", test::rand);
return 0;
}
• C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量 / 函数 / 类型出处(声明或定义)的逻辑,所以有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。
• namespace只能定义在全局,当然他还可以嵌套定义。
namespace test
{
int a = 10;
int Add(int a, int b)
{
return a + b;
}
int rand = 20;
namespace test01
{
int a = 29;
int Add(int a, int b)
{
return a - b;
}
}
}
int main()
{
printf("%d \n", test::rand);
printf("%d \n", test::a);
printf("%d \n", test::test01::a);
printf("%d \n", test::Add(1, 2));
printf("%d \n", test::test01::Add(1, 2));
return 0;
}
• 项目工程中多文件中定义的同名namespace会认为是一个namespace,不会冲突。
• C++标准库都放在⼀个叫std(standard)的命名空间中。
3、命名空间的使用
编译查找⼀个变量的声明 / 定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以下面程序会编译报错。所以我们要使用命名空间中定义的变量 / 函数,有三种方式:
• 指定命名空间访问,项目中推荐这种方式。(即通过::来访问命名空间里的)
在 C++ 里,:: 是作用域解析运算符,其作用是明确指定要使用的变量、函数或者类属于哪个命名空间或者类。大白话就是:使用该命名空间里面的东西。
• using将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式。
#include<iostream>
namespace test
{
int a = 10;
int Add(int a, int b)
{
return a + b;
}
int rand = 20;
namespace test01
{
int a = 29;
int Add(int a, int b)
{
return a - b;
}
}
}
using test::a; //将a展开,全局可用
int main()
{
a = 20;
return 0;
}
• 展开命名空间中全部成员,项目不推荐,冲突风险很大,日常小练习程序为了方便推荐使用。
namespace test
{
int a = 10;
int Add(int a, int b)
{
return a + b;
}
int rand = 20;
namespace test01
{
int a = 29;
int Add(int a, int b)
{
return a - b;
}
}
}
using namespace test; //将test里面的所以内容全部展开,可作用全局
int main()
{
a = 20;
Add(1, 2);
return 0;
}
二、C++输入&输出
• 是InputOutputStream的缩写,是标准的输入、输出流库,定义了标准的输入、输出对象。
• std::cin 是istream类的对象,它主要面向窄字符(narrowcharacters(oftypechar))的标准输 入流。
• std::cout 是ostream类的对象,它主要面向窄字符的标准输出流。
• std::endl 是⼀个函数,流插入输出时,相当于插入⼀个换行字符加刷新缓冲区。
• << 是流插入运算符,>>是流提取运算符。 (C语言还用这两个运算符做位运算左移 / 右移)
• 使用C++输入输出更方便,不需要像printf/scanf 输入输出时那样,需要手动指定格式,C++的输入输出可以自动识别变量类型 (本质是通过函数重载实现的),其实最重要的是 C++的流能更好的支持自定义类型对象的输入输出。
• IO流涉及类和对象,运算符重载、继承等很多⾯向对象的知识,所以这⾥只是简单认识一下C++IO流的用法,后面会有专门来细讲 IO 流库。
• cout / cin / endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们。
• ⼀般日常练习中我们可以 using namespace std,实际项目开发中不建议。
• 没有包含<stdio.h>,也可以使用 printf 和 scanf,在包含< stdio.h> 间接包含了。vs系列编译器是这样的,其他编译器可能会报错。
cin / cout 可以自动识别数据的类型
#include<iostream>
using namespace std;
int main()
{
int a = 10;
double b = 20.2;
char c = 'x';
cout << a << " " << b << " " << c << endl;
cout << endl;
cin >> a >> b >> c;
cout << a << " " << b << " " << c << endl;
return 0;
}
在 io 需求比较高的地方,如部分大量输入的竞赛题中,加上以下 3 行代码,可以提高 C++IO 效率(这里只是提一下):
#include<iostream>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
return 0;
}
三、缺省参数
• 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数。(有些地方把缺省参数也叫默认参数)
#include<iostream>
using namespace std;
void Fun(int a = 20) //给一个默认的值,没有传过来a的话,就使用默认值
{
cout << a << endl;
}
int main()
{
Fun(); //打印:20 没有传参使用默认值
Fun(10); //打印:10
return 0;
}
• 全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左 依次连续缺省,不能间隔跳跃给缺省值。
• 带缺省参数的函数调⽤,C++规定必须从左到右依次给实参,不能跳跃给实参。
#include<iostream>
using namespace std;
void Fun1(int a = 1, int b = 2, int c = 3) //全缺省
{
cout << a << " " << b << " " << c << endl;
}
void Fun2(int a, int b = 20, int c = 30) //半缺省
{
cout << a << " " << b << " " << c << endl;
}
int main()
{
Fun1(); // 1 2 3
Fun2(1); // 1 20 30 半缺省,必须传入没有缺省的变量的值
Fun2(1, 4); //1 4 30
Fun2(1, 4, 5); // 1 4 5
return 0;
}
• 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省 值。(避免函数声明和定义时的缺省参数的值不一样)
正确示例:
在Add.cpp中(定义):
int Add(int a, int b)
{
return a + b;
}
在Add.h中(声明):
int Add(int a = 10, int b = 20);
在test.cpp中测试:
#include<iostream>
#include"Add.h"
using namespace std;
int main()
{
int ret = Add();
cout << ret << endl; //30
return 0;
}
四、函数重载
C++支持在同⼀作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者 类型不同。这样C++函数调用就表现出了多态行为,使用更灵活。C语言是不支持同⼀作用域中出现同名函数的。
1、参数类型 / 顺序不同
#include<iostream>
using namespace std;
int Add(int a, int b)
{
return a + b;
}
double Add(double a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
int main()
{
cout << Add(1, 1) << endl; // 2
cout << Add(1.2, 1) << endl; // 2.2
cout << Add(1.3, 1.3) << endl; // 2.6
return 0;
}
2、参数个数不同
#include<iostream>
using namespace std;
int Add(int a, int b)
{
return a + b;
}
int Add(int a, int b, int c)
{
return a + b + c;
}
int main()
{
cout << Add(1, 1, 2) << endl; //4
cout << Add(1, 1) << endl; //2
return 0;
}
3.注意:
返回值不同,不能作为函数重载的条件