【C++ 系列笔记】05 C++ 类型转换、异常处理、I/O 流

补充知识

C++ 类型转换

静态转换static_cast<>()

允许转换指针、引用及普通变量

  • 基础类型

    double b = static_cast<double>(a);
    
  • 自定义类型

    // 向下转换 不安全
    Child* child = static_cast<Child*>(base);
    
    // 向上转换 安全
    Base& base = static_cast<Base&>(child);
    

    仅允许在父子类之间转换,转换成其他类型会报错

动态转换dynamic_cast<>()

仅允许转换指针和引用

  • 基础类型

    不允许基础类型的转换,如下方代码是错误的:

    double b = dynamic_cast<double>(a);
    
  • 自定义类型

    仅允许父子间转换,

    允许向上转换或发生了多态的转换。

    • 向上转换:(一直允许)

      Base& base = dynamic_cast<Base&>(child);
      
    • 向下转换:(不允许)

    • 发生多态:(允许)

      class Base{
         public:
          virtual void method();
      };
      class Child: public Base{
         public:
          virtual void method();
      };
      
      // 多态
      Base* base = new Child;
      Child* child = dynamic_cast<Child*>(base);
      

常量转换const_cast<>()

仅允许转换指针和引用

对基础类型和自定义类型的规则都相同。

  • 去掉const修饰

    const int* p;
    int* p1 = const_cast<int*>(p);
    
  • 添加const修饰

    int& ref;
    const int& ref1 = const_cast<const int&>(ref);
    

重新解释转换reinterpret_cast<>()

几乎不会用到。

语法相同,可以任意转换,没有安全性。

C++ 异常处理

基本语法

  • 关键字:try catch throw

  • 抛出异常

    void fun(){
        // ...
        if (/* condition */){
            throw error;
        }
    }
    
  • 捕获异常

    捕获某类型异常

    try {
        // 调用
    } catch (int error) {
        // 处理
    } catch (char error){
        // 处理
    }
    

    捕获各种异常

    try {
        // 调用
    } catch (...) {
        // 处理
    }
    

    异常必须被捕获,否则就会自动调用terminate中断程序。

    如果不想处理,可以捕获后继续抛出给上级调用者。

异常基本处理

  • 栈解旋

    即抛出异常之前,栈上的对象都会被释放掉。

  • 异常的接口声明

    以一种声明方式告知调用者,该接口会不会抛出异常,会抛出什么类型的异常。

    • 下面的例子表示:该接口仅抛出intdouble类型的异常。

      int fun() throw(int, double){
          // ...
      }
      
    • 还可以声明该接口不会抛出任何异常:

      int fun() throw(){
          // ...
      }
      
  • 异常变量的生命周期

    抛出引用:从抛出异常到捕获异常及处理的整个过程。

    抛出值:从抛出异常到捕获异常及处理的整个过程,不过增加了开销。

    抛出指针:指向的数据在捕获到并处理之前就被释放掉了。此时可以在堆区开辟空间存放,但存在内存泄漏的危险。

    一般都会抛出引用

  • 异常类

    可以定义一个异常类,用来进行异常的抛出、识别和处理。

    class Exception{
        // ...
    };
    
  • 多态中的异常

    class Exception{
       public:
        virtual string getMessage() = 0;
    };
    // 继承
    class OutOfRangeException: public Exception{
       public:
        virtual string getMessage(){
         	return string("越界异常");   
        }
    };
    
    void fun(){
    	// ...
        if(/* condition */){
            // 抛出异常
            throw OutOfRangeException();
        }
    }
    
    int main(){
        try {
            fun();
        } catch(Exception& error) {
            // 多态
            cout << error.getMessage() << endl;
        }
    }
    

C++ 标准异常库

C++ 标准库提供了一个异常类exception

#include <stdexcept>

  • 使用

    捕获到的异常对象拥有一个what方法,返回抛出者传递的字符传信息。

    void fun() {
    	throw std::out_of_range("nmsl, cnm.");
    }
    int main() {
        try {
            fun();
        } catch (std::out_of_range& error) {
            cout << error.what() << endl;
        }
    }
    

    也可以通过多态去捕获异常:

    try {
        fun();
    } catch (std::exception& error) {
        cout << error.what() << endl;
    }
    
  • 继承

    继承标准库提供的异常类

    class Exception {
       private:
        string message;
       public:
        Exception(string message): message(message){}
        // 重写 what
        virtual const char* what() const{
            return this->message.c_str;
        }
    }
    

C++ I/O 流

基本介绍

  • C++ I/O 的继承关系

io

另外还有:字符串 I/O 流strstream 文件 I/O 流fstream

  • coutcerrclog

    • cout(console output)

      cout 是标准输出流,除了向终端输出信息以外,还可以向磁盘文件中输出。

    • cerr(console error)

      cerr 是标准错误流,不会经过缓冲区,也不允许向文件中输出。

    • clog(console log)

      clog 也是标准错误流,不过它会经过缓冲区。

标准输入流

几个重要接口:

cin.get();                          // 输入一个字符
cin.get(char* buffer, int length);  // 输入一个字符串
cin.getline(char*);                 // 输入一个字符串
cin.ignore(char);                   // 忽略即将读入的字符
cin.peek();                         // 查看/偷窥 即将读入的字符

cin.fail();    // 获取错误状态
cin.clear();   // 重置错误状态
cin.sync();    // 清空缓冲区(存在问题)
  • cin.get

    int cin.get();
    

    一次读一个字符,与getchar()没什么区别。

    istream& get(char* buffer, int length);
    

    一次读一串字符,存入连续的内存中。不读末尾换行符\n,将其留在缓冲区,且遇到空格不会停止。

  • cin.ignore

    istream& ignore(int count = 1);
    

    丢弃/忽略马上读入的count个字符。

    istream& ignore(int count, char c);
    

    丢弃字符,直到数量达到 count个,或当期丢弃的字符与c相同。

  • cin.getline

    istream& getline(char* buffer, int length);
    

    一次读一串字符,存入连续的内存中。读末尾换行符\n,只不过不会读入内存中,而是直接丢弃,且遇到空格不会停止。

  • cin.peek

    int peek();
    

    偷窥,即读入一个字符,然后放回去。

  • cin.putback

    istream& putback(char c);
    

    将 c 中存放的字符放到输入缓冲区头部,即读入的下一个字符处。

  • cin.fail

    bool fail();
    

    用于获取 cin 的错误状态。返回 0 代表没有发生错误,1 代表发生了错误。

    例如用户输入一个 0 - 9 以外的字符,而我通过一个 int 来接收它,此时就会出错。

  • cin.clear

    用于重置 cin 的错误状态。

  • cin.sync

    用于清空缓冲区,不过在某些编译器上的实现并非清空缓冲区。

    例如 vc++ 2019。

    想要清空缓冲区可以这样做:

    cin.clear();
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
    

标准输出流

几个接口:(了解即可)

cout.flush();                               // 清空输出缓冲区
cout.put(char c);                           // 向缓冲区写一个字符
cout.write(char* buffer, int length);       // 向缓冲区写一个字符串
  • 格式化输出

    cout.width(int width);             // 输出长度
    cout.fill(char c);                 // 多余宽度通过 c 填充
    cout.setf(ios::_Fmtflags flags);   // 设置格式
    cout.unsetf(ios::_Fmtflags flags); // 写在格式
    cout.precision(int n);             // 设置实数精度为 n
    

    关于cout.setf,其还可以通过控制符来格式化输出:

    包含一个头文件:

    #include<iomanip>
    
    cout << setw(int width) // 输出长度
         << setfill(char c) // 多余宽度通过 c 填充
         << setiosflags(ios::_Fmtflags flags) // 设置格式
         << hex // 16 // 设置格式
         << oct // 8  // 设置格式
         << dec // 10 // 设置格式
         << endl;
    

    其较常用的参数如下:

    标志 说明
    ios::left 输出左对齐
    ios::right 输出右对齐
    ios::internal 符号位左对齐,数值右对齐,中间填充
    ios::dec 十进制
    ios::oct 八进制
    ios::hex 十六进制
    ios::showbase 输出前导符 ’0x‘ 或 ’0‘
    ios::showpointer 强制输出浮点数的小数点和尾数 0
    ios::showpos 对正数显示 + 号
    ios::scientific 浮点数以科学计数法的格式输出
    ios::fixed 浮点数以定点格式输出(小数形式)
    ios::unitbuf 每次输出之后刷新所有的流
    ios::stdio 每次输出之后清除 stdout stderr

文件 I/O 流

头文件:

#include <fstream>
  • 打开文件

    实例化时:

    ofstream ofs(const char* filename,ios::openmode _Mode );
    

    实例化后:

    ofstream ofs;
    ofs.open(const char* filename,ios::openmode _Mode );
    

    检验:

    if (!ofs.is_open()){
        // ...
    }
    

    关于 openmode:

    方式 说明
    ios::in 输入
    ios::out 输出(默认方式),若文件已存在则清空内容
    ios::app 输出,追加在末尾
    ios::ate 打开已有文件,文件指针指向文件尾
    ios::trunc 打开一个文件,文件存在则清空数据,文件不存在这建立新文件
    ios::binary 以二进制方式打开文件
    ios::nocreate 打开已有文件,若文件不存在则打开失败
    ios::noreplace 建立一个文件,若文件已存在则打开失败
    ios::in | ios::out 可读可写
    ios::out | ios::binary 以二进制方式打开一个输出文件
    ios::in | ios::binary 以二进制方式打开一个时输入文件
  • 写文件

    ofstream ofs("./write.txt", ios::out);
    ofs << "test" << endl;
    ofs.close();
    
  • 读文件

    ifstream ifs("./read.txt", ios::in);
    
    char buf[1024];
    while (ifs >> buf) {
        cout << buf << endl;
    }
    
    ifstream ifs("./read.txt", ios::in);
    
    char buf[1024];
    while (!ifs.eof()) {
        ifs.getline(buf, sizeof(buf));
        cout << buf << endl;
    }
    
    ifstream ifs("./read.txt", ios::in);
    
    char c;
    while (!ifs.eof()) {
        c = ifs.get();
        cout << c;
    }
    
    // 或者
    while ( (c = ifs.get()) != EOF ) {
        cout << c;
    }
    

字符串 I/O 流

头文件:

#include <sstream>

可以向一个流中传递数据和拿取数据。

stringstream stream;
// 获取一个流

stream << "123";
// 向流中推送数据

stream >> var;
// 向流流中拿取数据

应用:将任意基础类型进行转换

template <class resultType, class paramType >
resultType transform(paramType input) {
  stringstream stream;
  resultType output;
  stream << input;
  stream >> output;
  return output;
}

例如:

int output = transform<int>("123");
cout << output;
// > 123
posted @ 2020-06-11 00:14  高厉害  阅读(121)  评论(0编辑  收藏  举报