前言
从今天开始陆续更新 C++ 的学习过程,希望能对其他人起到一定帮助,同时欢迎大家一起学习,相互帮助指出不足。
今天的主要内容是 C++ 的入门部分,了解一下命名空间,C++ 的输入输出以及不同于之前 C 语言的函数重载,欢迎大佬们指正。
目录
一、命名空间
在C/C++中,变量、函数、类等都是大量存在的,如果这些变量、函数和类的名称将都存在于全局作用域中,就会导致命名冲突。而命名空间则是一种声明、前缀,表示该名字是在具体的哪一个范围内使用。
例如:
学校里有两个人都叫张三,分别是数学系和计算机系,那么对其分别称呼就是“数学系的张三”、“计算机系的张三”。“数学系的”、“计算机系的”就类似于命名空间,表示对应的对象等是在哪个范围类使用,这样就避免的重名时不知道是哪个“人”的误会。
作用域限定符 ::
我们已经知道有命名空间的存在,那么如何去访问某个空间里的元素呢?
答案是作用域限定符 ‘ :: ’ ,即两个英文冒号连打,其作用就是标识 :: 之后的元素是属于 :: 前的空间的一个元素它的用法如下:
空间名 + :: + 变量名 / 函数名 / ...
例如:
#include<iostream> int main() { int x = 10; std::cout << x << std::endl; }
std 是 c++ 的一个标准命名空间,std::cout 便是其中一个关键字,类似于 c 语言中的 printf,当我们使用它时,加上 std:: 就表示它是属于 std 下的一个元素,系统因此就会去 std 这个空间下查找有无这样一个元素。
namespace 关键字
使用方法一:namespace + 空间名 + { 定义变量、函数、类等 } (不需要分号 ; )
值得注意的是 namespace 并不会改变其中函数、变量等的生命周期,即不会从全局变量变为局部变量。同时,namespace 可以在多个文件中同时使用,或者在同一个文件中不同位置进行定义,最后所有的同一空间内的定义将被编译器自动整合到一起。
例如:
#include<iostream> namespace xh { int x = 10; } void print_x() { std::cout << xh::x << std::endl; } int main() { print_x(); std::cout << xh::x << std::endl; }
x 在命名空间 xh 中被定义,那么之后在 main 函数和 print_x 函数中都可以直接使用它,则 x 还是属于全局变量。
此外,命名空间是可以重复嵌套的,结合最开始的张三的例子就是武汉大学计算机系的张三、武汉工程大学计算机系的张三,计算机系相对于学校而言就是一个命名空间的嵌套使用。
例如:
#include<iostream> namespace wit { namespace xh { int x = 20; } int x = 10; } void print_x() { std::cout << wit::x << ' ' << wit::xh::x <<std::endl; } int main() { print_x(); }
上图便是 xh 对于 wit 的嵌套定义,命名空间之类的嵌套也是用 :: 进行连接的,需要注意的是嵌套之后不能直接访问 xh::x,必须使用 wit::xh::x。
使用方法二:using namespace + 空间名 + ; (全部展开)
这样的用法表示,在该条声明的作用域内,所有的元素都是 std 下的元素,这样就不必在每个元素前都加上 std::
例如:
#include<iostream> void func() { std::cout << "hello world" << std::endl; } int main() { using namespace std; int x = 10; cout << x << endl; }
对于上方代码,using namespace std; 作用于主函数内,因此在 func 函数内的 cout 就必须改为 std:: cout,而假如 using namespace std; 作用范围是全局,则 func 函数内也可以省去std:: 直接使用 cout ,如下:
#include<iostream> using namespace std; void func() { cout << "hello world" << endl; } int main() { int x = 10; cout << x << endl; }
使用方式三:using + 空间名 + :: + 元素名; (部分展开)
当项目比较大的时候,如果直接使用 using namespace std; 之类的全部展开,更易造成命名冲突,因此诞生一种更有效的部分展开,只让常用的元素展开,不仅使用起来更加方便,同时增加了项目的安全性,当然日常练习中不用太在意这些。
例如:
#include<iostream> namespace wit { namespace xh { int x = 20; } int x = 10; } using std::cout; int main() { cout << wit::x << ' ' << wit::xh::x << std::endl; }
上图中展开了 std::cout 那么在其生命周期内,就可以直接使用 cout,而 endl 没用展开,那么使用的时候就需要添加 std:: 。
二、C++ 输入&&输出
cin 标准输入 && cout 标准输出
像比于之前的 C 语言,C++ 提供的更加强大的输入输出流,其中 cin 相当于之前的 scanf,cout 则相当于之前的 printf ,不过不同的地方在于 cin、cout 都可以直接识别元素的类型进行输出。
同时个人建议,并不是所有情况下都要使用 cout ,根据具体的情况交替使用 cin、cout、scanf、printf 才能更好地适应输入输出环境。
例如:
#include<iostream> using namespace std; int main() { int x = 10, y = 20; cout <<"x = " << x << " y = " << y << std::endl; printf("x = %d y = %d", x, y); }
类似于此,文字和变量交替出现的情况下,printf 就相对更好掌握格式,同时还有当需要限定输出的浮点数的小数位个数时,printf 也相对比 cout 更加简便。
<< 流插入运算符 && >> 流提取运算符
当我们使用 cin、cout 的时候通常会使用到 <<、 >>,即两个小于号或者两个大于号连用,这两个分别表示流插入和流提取,对应的理解就是 cin 从键盘读取到输入流之后插入到对应的变量之中,而将我们希望输出的插入到 cout 流中去,就会输出到屏幕上,需要注意的是两者不能弄反啦,不然编译器可是会报错的哦~
三、缺省参数
概念
缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,通常是使用传过来的实参值,但是若缺少实参但是存在默认值,那么就可以使用默认值。
需要注意的是,缺省参数必须从右开始设置默认值,默认也会从左开始补齐缺少的实参值,C语言不支持缺省函数。
例如:
#include<iostream> using namespace std; void print(int a, int b = 10, int c = 20, int d = 30) { cout << a << ' ' << b << ' ' << c << ' ' << d << endl; } int main() { print(0); print(0, 1); print(0, 1, 2); //print(0, , 2); 不可行 print(0, 1, 2, 3); }
如上图,print 函数后三个参数设置了默认值,那么在使用的时候就可以省去部分传参,但是由于第一个参数没有默认值,所以至少需要一个传参。而由于缺省参数必须从右开始设置,因此不可以省去 c 的默认值,同时传参时也不能跳过某一个参数,例如上图注释行。
全缺省函数与半缺省函数
当一个函数中所有的参数都设置了默认值,此时称该函数为全缺省函数;
当一个函数中的参数部分设置了默认值,此时称该函数为半缺省函数;
此外还需要注意的是,缺省函数不能同时在函数的声明和定义中同时出现!(小黄建议大家在函数声明中设置默认值哦)
例如:
#include<iostream> using namespace std; int x = 40 void print_1(int a = 10, int b = 10, int c = 20, int d = 30) { cout << a << ' ' << b << ' ' << c << ' ' << d << endl; } void print_2(int a, int b = x, int c = 20, int d = 30) { cout << a << ' ' << b << ' ' << c << ' ' << d << endl; } int main() { print_2(0); x = 10; print_2(0); print_1(); return 0; }
上图中的 print_1 就是全缺省函数,而 print_2 则是半缺省函数。缺省函数的默认值只能是常量或者全局变量哦。
四、函数重载
函数重载是函数的一种特殊情况,即同一作用域中声明几个功能类似的同名函数,这些同名函数的 形参列表(参数个数或类型或顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。
函数重载类型
参数类型不同
例如:
#include<iostream> using namespace std; int Add(int left, int right) { cout << "type of return is int" << endl; return left + right; } double Add(double left, double right) { cout << "type of return is double" << endl; return left + right; } int main() { cout << Add(10, 20) << ' ' << Add(10.0, 20.0) << endl; }
参数个数不同
例如:
#include<iostream> using namespace std; void f() { cout << "no number" << endl; } void f(int a) { cout << "number is " << a << endl; } int main() { f(); f(10); return 0; }
参数顺序不同
例如:
#include<iostream> using namespace std; void f(int a, char b) { cout << "f(int a,char b)" << endl; } void f(char b, int a) { cout << "f(char b, int a)" << endl; } int main() { f(10, 'a'); f('a', 10); return 0; }
缺省函数导致的二义性
前面我们已经知道对于缺省函数,我们可以根据定义适当省略传参,但是假设我们定义了两个函数,其中一个不需要参数,另一个为全缺省函数但是不传参,此时会发生什么呢?
例如:
#include<iostream> using namespace std; void f() { cout << "no number" << endl; } void f(int a =10, int b = 20) { cout << a << b << endl; } int main() { f(); return 0; }
此时系统便会报错,因为多个重载函数符合要求,可以说是第一个函数输出 “no number”,也可以是第二个缺省函数,输出 a、b 但是编译器无法确认,因此产生了报错。
返回值不同无法构成重载的原因(了解即可)
函数名修饰规则
为什么 C++ 支持函数重载而 C 语言不支持呢?实际上是由于 C++ 新添加了一条规则,就是函数名修饰规则,我们以 Linux 情况下的修饰进行解释。
例如:
#include<iostream> using namespace std; int Add(int left, int right) { cout << "type of return is int" << endl; return left + right; } char Add(char left, char right) { cout << "type of return is char" << endl; return left + right; } int main() { cout << Add(10, 20) << ' ' << Add(a, b) << endl; }
对于函数 Add 的重载,C++ 实际上对 Add 进行了修饰。对于第一个函数实际的函数名是 Z3Addii 即函数名原长度为 3 ,函数原名为 Add,参数列表为 ii (int, int)。类似的对于第二个函数,实际的函数名为 Z3Addcc。
返回值不同不能重载的真正原因
已经知道了函数名修饰规则,我们理论上对于返回值不同不能构成函数重载的原因有了一个基本的了解,但是理论上也存在另一个可能,就是对函数名修饰规则做一点点修改,使函数的返回值最后也可以对函数名的修改造成影响,那么是不是就可以进行函数重载了呢?
例如:
#include<iostream> using namespace std; int Add(int left, int right) { cout << "type of return is int" << endl; return left + right; } char Add(int left, int right) { cout << "type of return is char" << endl; return left + right; } int main() { cout << Add(10, 20) << endl; }
答案是否定的!
最根本的原因在于无法在调用函数的时候进行区分,如上图中,假设我们能够对返回值不同的函数名进行区分,但是当我们准备调用 Add 函数的时候,我们怎么确定调用的哪一个函数?C++ 可以确定实参的类型,但是无法确定返回值的类型,即使采用了不同的变量接受返回值,那么也可以理解为隐式的强制类型转换。因此,无法在调用的时候区分函数才是仅返回值不同的函数无法重载的根本原因。
欢迎来和小黄一起学习呀~