浅尝 cin 和 cout

137 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

对于C++的朋友,相信对于std::cinstd::cout 一定不陌生
那么,它们的内部实现是怎么样的呢?
为什么std::cin 不用去指定我们的读入类型呢?
为什么std::cout不用去指定我们的输出类型呢?

本文中,关注的是对流的处理部分,至于对于流本身,并不讨论,所以,我们的流采取字符数组的形式 ( 设计一个缓冲区不是本文的讨论范围


模板特化

不要觉得模板只是个屠龙技!

template <typename T> void fun(T x);
template <> void fun(int x) { std::cout << "int" << std::endl; }
template <> void fun(float x) { std::cout << "float" << std::endl; }
template <> void fun(double x) { std::cout << "double" << std::endl; }

特化是一个很好用的特性,我们可以根据特化,来衍生出不一样的行为

有什么用呢?

我们可以利用特化和类的指定特化类型,来达到识别对象的类型(当然,有弊端,必须我们实现类型特化的代码)

比如:

#include <bits/stdc++.h>
template <typename Type> struct type {
  friend std::ostream &operator<<(std::ostream &os, type);
};

std::ostream &operator<<(std::ostream &os, type<int>) { 
  return os << "int"; 
}
std::ostream &operator<<(std::ostream &os, type<float>) {
  return os << "float";
}
std::ostream &operator<<(std::ostream &os, type<double>) {
  return os << "double";
}

int main() {
  std::cout << type<int>() << type<float>() << type<double>() << std::endl;
  return 0;
}

上述代码我们就可以直接输出 intfloatdouble的字符串值

可能看到这里你会发现,这个好像也没什么用。

接下来,我们就试试写一个 cin 出来

cin 的实现

cin 的定义

其实,为了通用,它是一个 istream 对象

我实现时就不考虑泛化和接口了,直接粗暴的实现我们的 cin

class istream {
  FILE *steam_;

public:
  explicit istream(FILE *steam = stdin) : steam_(steam){};

  istream &operator>>(int &t) {
    fscanf(steam_, "%d", &t);
    return *this;
  }
  istream &operator>>(float &t) {
    fscanf(steam_, "%f", &t);
    return *this;
  }
  istream &operator>>(double &t) {
    fscanf(steam_, "%lf", &t);
    return *this;
  }
  istream &operator>>(char &t) {
    fscanf(steam_, "%c", &t);
    return *this;
  }
  istream &operator>>(char t[]) {
    fscanf(steam_, "%s", t);
    return *this;
  }
};
istream cin;

我们可以利用fscanf来帮助我们出来这些东西(流的出来和字符串转换不是我们的重点)

那么,如何自定义类型输出呢?

只要我们有函数定义,我们不就调用operator<<(istream &in, pack &cur)么!

struct pack {
  int a;
  int b;
  friend istream &operator>>(istream &in, pack &cur);
};
istream &operator>>(istream &in, pack &cur) {
  in >> cur.a;
  in >> cur.b;
  return in;
}

道理很简单,有函数定义就找得到就ok

至于pack里面的 friend 关键字,其实讲道理,这个关键字可以不加

( 只要你不访问内部成员

好了!
我们现在基本上实现了一个裸的cin,但是不能够过滤空白字符!
比起真正的 cin还是逊色非常多

下面,我们来看看cout的实现

cout 的实现

class ostream {
  FILE *steam_;

public:
  explicit ostream(FILE *steam = stdout) : steam_(steam){};
  friend void endl(ostream &os);
  friend void end(ostream &os);
  ostream &operator<<(int &t) {
    fprintf(steam_, "%d", t);
    return *this;
  }
  ostream &operator<<(float &t) {
    fprintf(steam_, "%f", t);
    return *this;
  }
  ostream &operator<<(double &t) {
    fprintf(steam_, "%lf", t);
    return *this;
  }
  ostream &operator<<(char &t) {
    fprintf(steam_, "%c", t);
    return *this;
  }
  ostream &operator<<(const char t[]) {
    fprintf(steam_, "%s", t);
    return *this;
  }
  ostream &operator<<(void (*cur)(ostream &)) {
    cur(*this);
    return *this;
  }
};
void endl(ostream &os) {
  putc('\n', os.steam_);
  fflush(os.steam_);
}
void end(ostream &os) { fflush(os.steam_); }

cin一样的实现,但是我们多了一点函数用于清空缓冲区!

endl 代表着要换行

end 代表着不换行

但两者都要刷新缓冲区

下面我们来看看如何使用这两个

使用

#include <bits/stdc++.h>
class istream {
  FILE *steam_;

public:
  explicit istream(FILE *steam = stdin) : steam_(steam){};

  istream &operator>>(int &t) {
    fscanf(steam_, "%d", &t);
    return *this;
  }
  istream &operator>>(float &t) {
    fscanf(steam_, "%f", &t);
    return *this;
  }
  istream &operator>>(double &t) {
    fscanf(steam_, "%lf", &t);
    return *this;
  }
  istream &operator>>(char &t) {
    fscanf(steam_, "%c", &t);
    return *this;
  }
  istream &operator>>(char t[]) {
    fscanf(steam_, "%s", t);
    return *this;
  }
};
class ostream {
  FILE *steam_;

public:
  explicit ostream(FILE *steam = stdout) : steam_(steam){};
  friend void endl(ostream &os);
  friend void end(ostream &os);
  ostream &operator<<(int &t) {
    fprintf(steam_, "%d", t);
    return *this;
  }
  ostream &operator<<(float &t) {
    fprintf(steam_, "%f", t);
    return *this;
  }
  ostream &operator<<(double &t) {
    fprintf(steam_, "%lf", t);
    return *this;
  }
  ostream &operator<<(char &t) {
    fprintf(steam_, "%c", t);
    return *this;
  }
  ostream &operator<<(const char t[]) {
    fprintf(steam_, "%s", t);
    return *this;
  }
  ostream &operator<<(void (*cur)(ostream &)) {
    cur(*this);
    return *this;
  }
};
istream cin;
ostream cout;
void endl(ostream &os) {
  putc('\n', os.steam_);
  fflush(os.steam_);
}
void end(ostream &os) { fflush(os.steam_); }
struct pack {
  int a;
  int b;
  friend istream &operator>>(istream &in, pack &cur);
  friend ostream &operator<<(ostream &out, pack &cur);
};
istream &operator>>(istream &in, pack &cur) {
  in >> cur.a;
  in >> cur.b;
  return in;
}

ostream &operator<<(ostream &out, pack &cur) {
  out << cur.a;
  out << " ";
  out << cur.b;
  out << endl;
  return out;
}
int main() {
  pack a, b;
  cin >> a >> b;
  cout << a << b;
  return 0;
}