
🔥小叶-duck: 个人主页
❄️个人专栏:《Data-Structure-Learning》
✨未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游
目录
前言
在上一文章C++入门基础指南:命名空间namespace我们开启了C++学习的第一篇,主要是讲解了C++入门基础中的命名空间namespace,本篇文章我会接着讲解入门基础的后续知识。
一、C++输入&输出
<iostream> 是 Input Output Stream 的缩写,是标准的输入、输出流库,定义了标准的输入、输出对象。
cpp
#include<iostream>
using namespace std;
int main()
{
int a = 0;
cout << a << endl;
return 0;
}
在上一篇文章开始我们就见过这些东西,cout 以及 endl 其实就是属于命名空间 std 的内容,而命名空间 std 是在 <iostream>这个头文件中,使用如果我们要使用 cout 以及 endl ,就需要包含该头文件以及对命名空间 std 进行展开。
std::cin 是 istream类的对象,它主要面向窄字符(narrow characters(of type char))的标准输入流。
std::cout 是 ostream 类的对象,它主要面向窄字符的标准输出流。
cpp
#include<iostream>
using namespace std;
int main()
{
int a = 0;
cout << a << endl;
double b = 0.1;
char c = 'a';
cin >> b >> c >> a;
cout << b << " " << c << " " << a << endl;
return 0;
}

需要注意的是:不管是 >> 还是 << ,之间只允许有一个类型变量 ,比如 >> b c >> 这样就是错误的,但是 cin 和 cout 都支持连续输入输出,如上述代码所示。
std::endl 是一个函数,流插入输出时,相当于插入一个换行字符加刷新缓冲区。
cpp
#include<iostream>
using namespace std;
int main()
{
int a = 0;
cout << a << endl;
cout << a << "\n";
return 0;
}

由打印结果所示 endl 的确可以把其看成换行符 \n ,但也只能相当于看成一个换行符,endl 其实是一个很复杂的函数,由于C++后续知识我们还未讲解,在这里还无法去详细讲解这个函数,感兴趣的可以先自行了解。
<< 是流插入运算符,>> 是流提取运算符。(C语言还用这两个运算符做位运算左移/右移)
使用 C++ 输入输出更方便,不需要像 printf/scanf 输入输出时那样,需要手动指定格式, C++ 的输入输出可以自动识别变量类型 (本质是通过函数重载 实现的,这个以后会为大家进行讲解),其实最重要的是 C++ 的流能更好的支持自定义类型对象的输入输出。
cpp
#include<iostream>
using namespace std;
int main()
{
int a = 0;
cout << a << endl;
cout << &a << endl;
return 0;
}

IO流 涉及类和对象 ,运算符重载 、继承等很多面向对象的知识,这些知识我们还没有进行讲解,所以这里我们只能简单认识一下C++IO流的用法,后面我们会有专门花时间来仔细讲解IO流库。
cout/cin/endl 等都属于C++标准库 ,C++标准库都放在一个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们。
一般日常练习中我们可以using namespace std,实际项目开发中不建议using namespace std。
这里我们没有包含 <stdio.h> ,也可以使用 printf 和 scanf ,在包含 <iostream> 间接包含了。vs系列编译器是这样的,其他编译器可能会报错。
cpp
#include<iostream>
using namespace std;
int main()
{
int a = 0;
cout << a << endl;
printf("%d\n", a);
//没有包含 <stdio.h>,也可以使用 printf 和 scanf ,在包含 <iostream> 也间接包含了 <stdio.h>
return 0;
}

二、缺省参数
缺省参数 是声明或定义 函数时为函数的参数指定一个缺省值 。在调用该函数时,如果没有指定实参 则采用该形参的缺省值 ,否则使用指定的实参 ,缺省参数分为全缺省 和半缺省参数。(有些地方把缺省参数也叫默认参数)
cpp
#include<iostream>
using namespace std;
void Func(int a = 0)
{
cout << a << endl;
}
int main()
{
Func(); //没有传参时,使用参数的默认值
Func(10); //传参时,使用指定的实参
return 0;
}

全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续给予缺省值 ,不能间隔跳跃给缺省值。
cpp
#include <iostream>
using namespace std;
// 全缺省
void Func1(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << "\n" << endl;
}
// 半缺省
void Func2(int a, int b = 10, int c = 20)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << "\n" << endl;
}
int main()
{
Func1();
Func1(1);
Func1(1, 2);
Func1(1, 2, 3);
Func2(100);
Func2(100, 200);
Func2(100, 200, 300);
return 0;
}

不按照C++规定的半缺省参数示例:
cpp
void Func1(int a = 10, int b = 20, int c)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << "\n" << endl;
}

带缺省参数 的函数调用 ,C++规定必须从左到右依次给实参 ,不能跳跃给实参。
函数声明 和定义分离 时,缺省参数不能在函数声明和定义中同时出现 ,规定必须函数声明给缺省值。
以栈的初始化举例:
cpp
//Stack.h
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
namespace Stack
{
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* ps, int n = 4);
//半缺省参数,当没有传第二个实参则默认n为4,如果传了第二个实参则n为实参值
//省去了不断额外开辟空间带来的损耗,缺省参数在之后C++学习中会非常好用
}
//Stack.c
#include "Stack.h"
namespace Stack
{
void STInit(ST* ps, int n)
{
assert(ps);
ps->a = (STDataType*)malloc(n * sizeof(STDataType));
ps->top = 0;
ps->capacity = n;
//当没有传第二个实参则栈开辟空间大小默认为4,如果传了第二个实参则为实参值
}
}
//Test.c
#include<iostream>
using namespace std;
#include"Stack.h"
int main()
{
Stack :: ST st1;
//Stack::STInit(&st1);//没有第二个实参则默认开辟空间大小为4
Stack :: STInit(&st1, 1000);//缺省参数能当已知开辟空间的大小后可直接传第二个形参,
//使其直接开辟对应大小的空间,无需后续额外开辟空间导致效率的降低
return 0;
}
并且从栈的初始化这个例子我们就会发现缺省参数非常的好用 ,在之前用C语言实现数据结构时,我们对栈的初始化都是使其空间为0,当有数据传入时才会额外开辟空间 ,但是我们会发现随着数据的不断传入 我们就需要不断的开辟空间 ,这就会导致损耗 ;但我们利用C++中的缺省参数就可以解决这个问题了,当我们明确知道要传入数据的个数时则可以在栈的初始化中直接把空间开辟好,后续就无需再额外开辟空间了。
三、函数重载
在C语言中我们知道一个加法函数只能对一种类型进行操作,如果要同时对多种类型如果小数加法或者小数与整数加法进行操作则需要定义不同的函数 来实现,这样的确比较麻烦。
所以C++解决了这个问题,就叫做函数重载 :C++支持在同一作用域 中出现同名函数 ,但是要求这些同名函数的形参不同 ,可以是参数个数不同 或者类型不同 。这样C++函数调用 就表现出了多态行为 ,使用更灵活。C语言是不支持同一作用域中出现同名函数的。
cpp
#include<iostream>
using namespace std;
//1、参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
//2、参数个数不同
void f(int a)
{
cout << "f(int a)" << endl;
}
void f(int a, int b)
{
cout << "f(int a, int b)" << endl;
}
//3、参数类型顺序不同
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()
{
int add1 = Add(10, 20);
cout << add1 << endl;
double add2 = Add(10.1, 20.2);
cout << add2 << endl;
f(10);
f(10, 20);
f(10, 'a');
f('a', 10);
return 0;
}

通过打印的结果我们就会发现C++的功能比C强大多了,只要形参不同就能找到对应形参的同名函数。
但是返回值的不同 是无法作为重载的条件,因为在函数调用时无法区分:
cpp
//返回值不同不能作为重载条件,因为调用时也无法区分
void fxx(int a)
{
cout << "void f(int a)" << endl;
}
int fxx(int a)
{
cout << "int f(int a)" << endl;
return 0;
}

但是如果两个函数已经构成重载,也不一定能实现函数调用:
cpp
//下面两个函数构成重载
//但是函数调用f1时,会报错,存在歧义,编译器不知道调用谁
void f1()
{
cout << "f1()" << endl;
}
void f1(int a = 10)
{
cout << "f1(int a)" << endl;
}
int main()
{
f1();
return 0;
}

原因还是在于缺省参数能允许没有实参进行传递 ,这就会导致 f1(); 既能进入第一个f1函数也能进入第二个f1函数,编译器无法区别而导致报错。
但是这种类似的情况也有办法解决,就是借助命名空间域 将两者进行分离,但此时就不符合函数重载必须保证两个同名函数在同一作用域中的条件了。
结束语
到此,C++入门基础中的输入输出、缺省参数以及函数重载就讲解完了,但是入门基础仍有一些知识点还没有讲解,下一篇文章我们将对C++入门基础进行收尾。希望这篇文章对大家学习C++有所帮助!
C++参考文档:
https://legacy.cplusplus.com/reference/
https://zh.cppreference.com/w/cpp
https://en.cppreference.com/w/