zoukankan      html  css  js  c++  java
  • 【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);
      

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

    • cin.ignore

      istream& ignore(int count = 1);
      

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

      istream& ignore(int count, char c);
      

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

    • cin.getline

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

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

    • 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(), '
      ');
      

    标准输出流

    几个接口:(了解即可)

    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
    
  • 相关阅读:
    软件工程基础 结对项目作业
    软件工程基础 第3次个人作业
    软件工程基础 第2次个人作业
    软件工程基础 第1次个人作业
    BUAA_2019_MATLAB基础与应用_期末复习纲要
    BUAA_2019_数学建模_期末复习提纲
    技术学习资源
    线程优先级的高低和执行顺序的关系
    阿里和Google的JAVA开发规约
    java中创建线程的3种方法
  • 原文地址:https://www.cnblogs.com/gaolihai/p/13149743.html
Copyright © 2011-2022 走看看