前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >c++中endl操作符以及它的兄弟们

c++中endl操作符以及它的兄弟们

作者头像
cpp加油站
发布2021-05-14 11:16:10
3930
发布2021-05-14 11:16:10
举报
文章被收录于专栏:cpp加油站

说明一下,我用的是g++7.1.0编译器,标准库源代码也是这个版本的。

一直以来,我们每次使用cout输出数据的时候,如果要换行,都知道使用形如cout << endl;这样的形式,那么endl到底是什么呢,它是怎么样实现输出一个换行符的功能的,以前我没有思考过,但现在我想弄懂它,下面就一起看一下吧。

1.endl操作符的实现

在标准库头文件<ostream>中,我找到了endl的操作符重载函数,如下:

代码语言:javascript
复制
template<typename _CharT, typename _Traits>
    inline basic_ostream<_CharT, _Traits>&
    endl(basic_ostream<_CharT, _Traits>& __os)
    { return flush(__os.put(__os.widen('\n'))); }

它是一个内联的函数模板,是一个全局的函数模板,endl正是它的函数名,它的作用是往缓冲区写入一个换行符并且立即从缓冲区刷新到外部设备中。

那么endl是怎么与<<操作符关联起来的呢,我们在ostream头文件ostream类的声明中又发现了以下代码:

代码语言:javascript
复制
__ostream_type&
      operator<<(__ostream_type& (*__pf)(__ostream_type&))
      {
    // _GLIBCXX_RESOLVE_LIB_DEFECTS
    // DR 60. What is a formatted input function?
    // The inserters for manipulators are *not* formatted output functions.
    return __pf(*this);
      }

这个操作符的入参是一个__ostream_type& (*__pf)(__ostream_type&)类型的函数指针,这个类型与endl的类型是一致的,从而,我们现在知道了endl的实现过程。

与endl同样实现的总共是亲兄弟三个,他们类型一样,且都是对缓冲区进行操作,如下:

操作符

说明

endl

输出一个换行符到缓冲区,且即时刷新到外部设备

ends

输出一个空字符到缓冲区

flush

调用flush函数,把数据从缓冲区刷新到外部设备

2. 格式化操作符

说完endl的亲兄弟,接下来说一说它的堂兄弟们,那就是格式化操作符,在某些书籍上也叫做操纵算子,操纵算子用法与endl一样,也是形如cout << oct这样的形式,但它不会对缓冲区直接进行操作,它是对后续的数据输出进行格式化,类似c语言的%d一样,且操纵算子的实现方式与endl类似,只是<<的返回类型与参数类型不一样而已,这里就不再多说。

操纵算子分为两类,一类是无参的,定义在ios_base.h头文件中,还有一类是有参的,定义在iomanip头文件中。

2.1 无参操纵算子

操纵算子

说明

boolalpha

针对bool类型变量,不是输出0和1,而是输出true或者false

noboolalpha

boolalpha的反向操作

showbase

在输出八进制或者十六进制的时候,加上0x这样的前缀,主要它要放在进制操作符的前面

noshowbase

showbase的反向操作

showpoint

强制打印小数点

noshowpoint

showpoint的反向操作

showpos

针对非负的数字,强制加上+号输出

noshowpos

showpos的反向操作

skipws

它是一个输入类操作符,作用是在输入时跳过空格,这一点与不使用skipws时是一致的

noskipws

这里主要是noskipws会改变>>的默认输入方式,会把空格,制表符等也读入

uppercase

在输出十六进制这样的数据时,对里面的字母进行大写,注意它对输出字符类型或者字符串类型是不起作用的

nouppercase

uppercase的反向操作

unitbuf

每次输出以后都刷新缓冲区

nounitbuf

unitbuf的反向操作

internal

在设置了输出宽度的情况下,符号左对齐,值右对齐,中间使用空格填充

left

在设置了输出宽度的情况下,输出整体左对齐,没有设置输出宽度,说对齐都是耍流氓

right

在设置了输出宽度的情况下,输出整体右对齐,iostream流默认右对齐

dec

十进制输出,对浮点数不起效果,只对整型有效果

hex

十六进制输出,对浮点数不起效果,只对整型有效果

oct

八进制输出,对浮点数不起效果,只对整型有效果

fixed

定点十进制进行输出,默认输出6位小数位,小数位不足补0,超出的四舍五入,保留6位

scientific

科学计数法输出

hexfloat

十六进制形式输出浮点数

defaultfloat

对浮点数输出恢复默认状态

一个使用案例如下:

代码语言:javascript
复制
#include <iostream> 

using namespace std; 

int main() 
{ 

    // Initializing the integer 
    double x = 10.2333336; 

    //将浮点数x以十六进制形式输出,且字母都为大写
    cout  << uppercase << hexfloat << x << endl; 

    cout.width(12);
    double y = -11.222;
    //取消指定的浮点数格式,按默认格式输出
    cout << defaultfloat;

    //符号左对齐,数值右对齐,中间填充空格
    cout << internal << y << endl;

    return 0; 
}

输出结果如下:

代码语言:javascript
复制
0X1.47777806A1DABP+3
-     11.222
2.2 有参操纵算子

有参的操纵算子实际上是在无参的基础上实现的,是对无参操纵算子的补充,且对无参操纵算子的使用起到了简化的作用。

首先还是看一看有参操纵算子有哪些,如下:

操纵算子

参数类型

说明

resetiosflags

ios_base::fmtflags,此类型是一个枚举类型,包含了上述的无参操纵算子,多的格式之间以单竖线分隔

输入输出都可使用,重置当前流的格式

setiosflags

ios_base::fmtflags

输入输出都可使用,增加当前流的格式

setbase

int

输入输出都可使用,设置进制,参数值可为8,10,16,如果是其他值则表示使用默认的

setfill

无固定类型,是一个函数模板

输入输出都可使用,设定对齐时的填充字符,虽说是模板,但参数一般建议使用char类型

setprecision

int

输入输出都可使用,设置精度,注意默认情况下这里的精度并不是指小数位,而是包含整数位在内,总共可以显示多少位数字,但是如果事先使用fixed指定了的话,那该精度就是单指小数位了

setw

int

输入输出都可使用,设置宽度

get_money

有两个参数,第一个参数是一个函数模板,但根据iomanip头文件,它应该是一个long double类型或者string类型,此时该参数其实是一个出参,输入的数据存储在该参数里面,第二个参数是一个bool类型,表示是否国际化

输入使用,根据设置的区域文化和编码以及输入的对应的货币表达式,获取相应的数据

put_money

有两个参数,第一个参数是一个函数模板,但根据iomanip头文件,它应该是一个long double类型或者string类型,第二个参数是一个bool类型,表示是否国际化

输出使用,根据设置的区域文化和编码,输出相应的货币表达式

put_time

第一个参数是const std::tm类型指针,第二个类型是对时间进行格式化的格式字符串

根据第二个参数指定格式输出tm中数据

get_time

第一个参数是const std::tm类型指针,第二个类型是对时间进行格式化的格式字符串

根据第二个参数指定格式把数据填充到tm中

带参数的这些操作函数,前面6个其实是比较好理解的,但是后面四个用起来就比较麻烦了,而且单独使用也是不起作用的,下面我们就后面四个操作符,看一下使用案例,如下:

代码语言:javascript
复制
#include <iomanip>
#include <iostream>
#include <time.h>
#include <sstream>
using namespace std;

void test01()
{
  locale loc_de = locale("en_US.utf-8");
  cout.imbue(loc_de);

  const string str("720000000000");
  cout << put_money(str) << endl;

  string str2;
  cin.imbue(loc_de);
  cin >> get_money(str2);//这里要按照imbue设置的区域和编码进行输入,形如:72,000,000
  cout << "str2=" << str2 << endl;

  time_t t;
  time(&t);
  tm *tmp = localtime(&t);
  cout << put_time(tmp, "%y %a") << endl;

  tm time1;
  istringstream iss("15:12:00 2021");
  iss >> get_time(&time1, "%H:%M:%S %Y");
  cout << "hour:" << time1.tm_hour << ',' << "min:" << time1.tm_min << ',' << "sec:" << time1.tm_sec << endl;
}

int main()
{
  test01();
  return 0;
}

输出显示如下:

代码语言:javascript
复制
[root@mylinux ~]# ./a.out
7,200,000,000.00
12,00,00       #注意这里是屏幕输入的
str2=120000
21 Thu
hour:15,min:12,sec:0
[root@mylinux ~]#

后面四个函数的使用就涉及到程序国际化以及区域文化的问题,比如浮点数,在我们大中国是72000.12,那么到了美国可能又是用72,000.12来表示,关于区域文化,这里就不展开说明了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 cpp加油站 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.endl操作符的实现
  • 2. 格式化操作符
    • 2.1 无参操纵算子
      • 2.2 有参操纵算子
      相关产品与服务
      数据保险箱
      数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档