前言:C++——为弥补C缺陷而生的语言
C++起源于 1979 年,当时 Bjarne Stroustrup 在贝尔实验室工作,面对复杂软件开发任务,他感到 C 语言在表达能力、可维护性和可扩展性方面存在不足。
1983 年,Bjarne Stroustrup 在 C 语言基础上添加面向对象编程特性,设计出 C++雏形,有了类、封装、继承等核心概念。因此,C++是兼容了C语言的(用过Dev-C++的同学应该不难理解,文件后缀明明是.cpp却可以编译C语言)。
C++的标准化工作于 1989 年开始,1994 年提出第一个标准化草案。之后联合标准化委员会投票通过将惠普实验室开发的 STL 包含到 C++标准中,这延缓了 C++标准化进程。
1997 年 11 月 14 日,通过最终草案,1998 年,C++的 ANSI/IS0 标准投入使用。总之,C++是在 C 语言基础上发展而来,弥补了 C 语言的一些不足。
该文将深入剖析cout和cin两个基本的输入输出函数及其对应运算符,阐述输入输出流的概念,为日后的学习打下牢靠基础。
C++的输入与输出
1.引子:C++的第一个程序
按照传统惯例,第一个程序当然是Hello World了。我们来写一下代码:
// 这⾥的std cout等看不懂没关系,下⾯我们会依次讲解
#include<iostream>
using namespace std;
int main()
{
cout << "hello world\n" << endl;
return 0;
}
虽然还没学过,但相信以你的聪明才智,不难看出cout就是输出语句吧。下面我们来正式学习一下。
2.C++的输入与输出
- 是 Input Output Stream 的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输 出对象。
- std::cin 是 istream 类的对象,它主要⾯向窄字符(narrow characters (of type char))的标准输 ⼊流。
- std::cout 是 ostream 类的对象,它主要⾯向窄字符的标准输出流。
- std::endl 是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区。
- <<是流插入运算符,>>是流提取运算符。(C语⾔还⽤这两个运算符做位运算左移/右移)(疑难解答2)
- 使⽤C++输⼊输出更⽅便,不需要像printf/scanf输⼊输出时那样,需要⼿动指定格式,C++的输⼊ 输出可以⾃动识别变量类型,其实最重要的是 C++的流能更好的⽀持⾃定义类型对象的输⼊输出。(疑难解答1)
- cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要 通过命名空间的使⽤⽅式去⽤他们。
- 这⾥我们没有包含<stdio.h>,也可以使⽤printf和scanf,这是因为<iostream>间接包含了<stdio.h>。vs系列编译器是这样的,其他编译器可能会报错。
好,下面让我们自己动手来写一个简单程序吧:
#include <iostream>
int main() {
int num;
std::cout << "请输入一个整数: ";
std::cin >> num;
std::cout << "您输入的整数是: " << num << std::endl;
return 0;
}
等等!为什么你的cout没有用endl就输出了?
在默认情况下,使用 std::cout
进行输出时,如果后面没有其他的输出操作紧跟,或者程序没有结束,输出通常会立即显示在屏幕上。
这是因为输出流有一个默认的刷新机制,对于一些简单的输出语句,即使没有手动使用 std::endl
或其他强制刷新的操作,系统也会适时地将输出内容发送到屏幕上,以提供及时的反馈。
但如果在复杂的程序环境中,或者有大量的连续输出操作,为了确保输出的及时性和准确性,使用 std::endl
或其他刷新缓冲区的操作会更加可靠。
那还有!为什么你的Hello用的是cout,这里却变成了std::cout?
问得好!这个就涉及到命名空间的问题了。你没发现这个程序比上一个程序还少了一个using namespace std;吗?
3.命名空间
在C/C++中,变量、函数和后⾯要学到的类都是⼤量存在的,这些变量、函数和类的名称将都存在于全 局作⽤域中,可能会导致很多冲突。使⽤命名空间的⽬的是对标识符的名称进⾏本地化,以避免命名 冲突或名字污染,namespace关键字的出现就是针对这种问题的。
namespace本质是定义出⼀个域,这个域跟全局域各自独立,不同的域可以定义同名变量,这样可以避免命名冲突。
在使用命名空间中的变量时,可以using namespace 命名空间,这样我们在使用命名空间的域中就可以使用该命名空间中的变量和函数;也可以是命名空间::变量名,这样虽是麻烦些,却有效避免了不同域中相同变量名冲突的情况。
举个例子:
这是我们直接用using namespace 的情况,我们主函数就可以使用命名空间bit_le的变量了。
#include <iostream>
namespace bit_le//创建一个命名空间
{
int a=798;
}
int main()
{
using namespace bit_le;
std::cout<<a;
return 0;
}
结果如下:
另一种方法如下:
#include <iostream>
namespace bit_le//创建一个命名空间
{
int a=798;
}
int main()
{
int a=666;
std::cout<<a<<std::endl;//输出主函数域中的a
std::cout<<bit_le::a;//输出命名空间“bit_le”中的a
return 0;
}
结果如下:
最后,不得不提的是⼀般⽇常练习中我们可以using namespace,实际项目开发中不建议using namespace。
为什么呢?明明看起来直接using namespace一下更简单。这是因为在做复杂项目时,变量名容易重复,导致冲突。我们再举例:
所以在写较长项目的时候,多敲几下,避免因为冲突造成的bug。
疑难解答
1.对输入输出流的一个基本概念
在编程中,“流”(Stream)是一种用于处理数据输入和输出的抽象概念
可以把流想象成一条数据的“河流”,数据在其中连续地流动。
从功能角度来看:
- 流提供了一种统一的方式来处理不同来源和去向的数据。
- 无论是从文件读取数据、从网络接收数据、从键盘获取用户输入,还是向屏幕输出、向文件写入数据等,都可以通过流来实现。
从特点角度来看:
- 流具有连续性和顺序性。数据按照一定的顺序依次通过流进行传输。
- 流可以是字节流(处理原始的字节数据)或字符流(处理字符数据)。
在 C++ 中,输入流用于从数据源获取数据,输出流用于将数据发送到数据目的地。
例如,std::cin
是标准输入流,它可以从键盘获取用户输入的数据。而 std::cout
是标准输出流,用于将数据输出到屏幕。
流的操作通常具有缓冲机制。这意味着数据可能不会立即被发送或接收,而是先存储在缓冲区中,等到缓冲区满或者遇到特定的刷新操作(如 std::endl
)时,才进行实际的传输。
在文件操作中,std::ifstream
用于读取文件,std::ofstream
用于写入文件,它们也被视为流。
总的来说,流的概念使得数据的输入和输出处理更加方便、灵活和统一,有助于提高编程的效率和可维护性。
2.从流当中存入及提取数据
不知道你看完流的概念有没有一个疑惑,反正我学的时候是有的。问题如下:
我们在使用cout函数进行输出时,似乎并没有向流中存入数据。怎么就从流中提取出来了呢?
使用
cout
进行输出时,实际上并不是没有事先存入数据。
cout
是输出流对象,它的工作方式是在您使用插入运算符<<
向它提供数据时,将数据暂时存储在内部的缓冲区中。当遇到特定的情况,比如输出换行符
'\n'
、遇到程序结束、缓冲区满或者手动调用刷新缓冲区的操作(如std::endl
)时,存储在缓冲区中的数据才会被实际输出到目标设备(通常是屏幕)。所以,虽然看起来是直接进行了输出,但实际上是先将数据存入了缓冲区,只是这个过程对于您来说是自动和隐式处理的。
我们来举个例子:
#include <iostream>
int main() {
std::cout << "Hello"; // 数据暂时存储在缓冲区
sleep(5);//停留5秒程序接着运行
std::cout << " World"; // 数据继续存储在缓冲区
std::cout << std::endl; // 遇到换行符,刷新缓冲区,输出之前存储的数据
return 0;
}
嗯~好,这上个问题刚解决,下个问题就出来了。
明明“out”是“出”,“in”是“入”。你说cout是插入用的,那cin是干嘛的?口说无凭,你得给我证明!
cout的确是输出语句,但要先用“<<”将内容存入流。遇到std::endl时输出之前存入的数据(什么?你说你经常不加endl也正常输出了?别急,马上讲)。
cin是输出语句,用“>>”将流中的数据赋值给变量。例如,如果要从标准输入读取一个整数并存储到变量 num
中,我们会这样写:cin >> num
,这里就像是从输入流中把数据“拉出来”并存放到 num
里。