美文网首页
C++ 快速入门笔记:进阶编程

C++ 快速入门笔记:进阶编程

作者: 云之外 | 来源:发表于2016-11-11 18:04 被阅读291次

    C++入门笔记:高级编程

    文件和流

    1. 打开文件

      void open (const char *filename, ios::openmode mode);
      
      • ios::app 追加模式。所有写入都追加到文件末尾
      • ios::ate 文件打开后定位到文件末尾
      • ios::in 打开文件用于读取
      • ios::out 打开文件用于写入
      • ios::trunc 如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。
    2. 关闭文件

      void close();
      
    3. 写入文件

      • 使用流插入运算符 ( << ),向 ofstream / fstream 流中写入信息
    4. 读取文件

      • 使用流提取运算符 ( >> ),向 ifstream / fstream 流中写入信息
    5. 文件读写实例

      #include <fstream>
      #include <iostream>
      using namespace std;
      int main ()
      {
         char data[100];
         ofstream outfile;
         outfile.open("afile.dat");
         cout << "Writing to the file" << endl;
         cout << "Enter your name: "; 
         cin.getline(data, 100);
         outfile << data << endl;
         cout << "Enter your age: "; 
         cin >> data;
         cin.ignore();
         outfile << data << endl;
         outfile.close();
         ifstream infile; 
         infile.open("afile.dat"); 
         cout << "Reading from the file" << endl; 
         infile >> data; 
         cout << data << endl;
         infile >> data; 
         cout << data << endl; 
         infile.close();
         return 0;
      }
      
    6. 文件位置指针

      • stream 和 ostream 都提供了用于重新定位文件位置指针的成员函数。

        • istream 的 seekg ("seek & get")
        • ostream 的 seekp ("seek & put")
        // 定位到 fileObject 的第 n 个字节(假设是 ios::beg)
        fileObject.seekg( n );
        
        // 把文件的读指针从 fileObject 当前位置向后移 n 个字节
        fileObject.seekg( n, ios::cur );
        
        // 把文件的读指针从 fileObject 末尾往回移 n 个字节
        fileObject.seekg( n, ios::end );
        
        // 定位到 fileObject 的末尾
        fileObject.seekg( 0, ios::end );
        

    异常处理

    1. try / catch / throw

      • try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。
      • catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
      • throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
    2. C++ 标准的异常

      异常 描述
      std::exception 该异常是所有标准 C++ 异常的父类。
      std::bad_alloc 该异常可以通过 new 抛出。
      std::bad_cast 该异常可以通过 dynamic_cast 抛出。
      std::bad_exception 这在处理 C++ 程序中无法预期的异常时非常有用。
      std::bad_typeid 该异常可以通过 typeid 抛出。
      std::logic_error 理论上可以通过读取代码来检测到的异常。
      std::domain_error 当使用了一个无效的数学域时,会抛出该异常。
      std::invalid_argument 当使用了无效的参数时,会抛出该异常。
      std::length_error 当创建了太长的 std::string 时,会抛出该异常。
      std::out_of_range 该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator
      std::runtime_error 理论上不可以通过读取代码来检测到的异常。
      std::overflow_error 当发生数学上溢时,会抛出该异常。
      std::range_error 当尝试存储超出范围的值时,会抛出该异常。
      std::underflow_error 当发生数学下溢时,会抛出该异常。
    3. 定义新的异常

      • 通过继承或重载 exception 类来定义新的异常
        #include <iostream>
        #include <exception>
        using namespace std;
        
        struct MyException : public exception
        {
            const char * what () const throw ()
            {
                return "C++ Exception";
            }
        };
         
        int main()
        {
            try
            {
                throw MyException();
            }
            catch(MyException& e)
            {
                std::cout << "MyException caught" << std::endl;
                std::cout << e.what() << std::endl;
            }
            catch(std::exception& e)
            {
                //其他的错误
            }
        }
        

    动态内存

    1. C++ 程序中的内存分为两个部分

      • 栈:在函数内部声明的所有变量都将占用栈内存。
      • 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。
    2. 可以使用 new 运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。

    3. 不需要动态分配内存时,可以使用 delete 运算符,删除之前由 new 运算符分配的内存。

    4. new 和 delete 运算符

      • 使用 new 运算符来为任意的数据类型动态分配内存

        // new data-type;
        // 如果自由存储区已被用完,可能无法成功分配内存。所以建议检查 new 运算符是否返回 NULL 指针。
        double* pvalue  = NULL;
        if( !(pvalue  = new double ))
        {
            cout << "Error: out of memory." <<endl;
            exit(1);
        
        }
        
      • 使用 delete 操作符释放它所占用的内存

        #include <iostream>
        using namespace std;
        
        int main ()
        {
           double* pvalue  = NULL; // 初始化为 null 的指针
           pvalue  = new double;   // 为变量请求内存
           *pvalue = 29494.99;     // 在分配的地址存储值
           cout << "Value of pvalue : " << *pvalue << endl;
           delete pvalue;         // 释放内存
           return 0;
        }
        
    5. 数组的动态内存分配

      int ROW = 2;
      int COL = 3;
      double **pvalue  = new double* [ROW]; // 为行分配内存
      
      // 为列分配内存
      for(int i = 0; i < COL; i++) {
          pvalue[i] = new double[COL];
      }
      for(int i = 0; i < COL; i++) {
          delete[] pvalue[i];
      }
      delete [] pvalue; 
      
    6. 对象的动态内存分配

      #include <iostream>
      using namespace std;
      
      class Box
      {
          public:
               Box() { 
                  cout << "调用构造函数!" <<endl; 
               }
               ~Box() { 
                  cout << "调用析构函数!" <<endl; 
               }
      };
      
      int main( )
      {
          Box* myBoxArray = new Box[4];
          delete [] myBoxArray; // Delete array
          return 0;
      }
      

    命名空间

    1. 命名空间可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。

    2. 定义命名空间

      #include <iostream>
      using namespace std;
      
      // 第一个命名空间
      namespace first_space{
          void func(){
              cout << "Inside first_space" << endl;
          }
      }
      // 第二个命名空间
      namespace second_space{
          void func(){
              cout << "Inside second_space" << endl;
          }
      }
      int main ()
      {
           
          // 调用第一个命名空间中的函数
          first_space::func();
             
          // 调用第二个命名空间中的函数
          second_space::func(); 
          
          return 0;
      

    }
    ```

    1. using 指令

      #include <iostream>
      using namespace std;
      
      // 第一个命名空间
      namespace first_space{
          void func(){
              cout << "Inside first_space" << endl;
          }
      }
      // 第二个命名空间
      namespace second_space{
          void func(){
              cout << "Inside second_space" << endl;
          }
      }
      using namespace first_space;
      int main ()
      {
           
          // 调用第一个命名空间中的函数
          func();
             
          return 0;
      }
      
      #include <iostream>
      using std::cout;
      
      int main ()
      {
          cout << "std::endl is used with std!" << std::endl;
         
          return 0;
      }
      
    2. 不连续的命名空间

      • 命名空间可以定义在几个不同的部分中,因此命名空间是由几个单独定义的部分组成的。一个命名空间的各个组成部分可以分散在多个文件中。
    3. 嵌套的命名空间

      #include <iostream>
      using namespace std;
          
      // 第一个命名空间
      namespace first_space{
          void func(){
              cout << "Inside first_space" << endl;
          }
          // 第二个命名空间
          namespace second_space{
               void func(){
                  cout << "Inside second_space" << endl;
               }
          }
      }
      using namespace first_space::second_space;
      int main ()
      {
          // 调用第二个命名空间中的函数
          func();
          return 0;
      }
      

    模板

    1. 模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。

    2. 模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector <int> 或 vector <string>。

    3. 函数模板

      template <class type> ret-type func-name(parameter list)
      {
         // 函数的主体
      } 
      
      • 在这里,type 是函数所使用的数据类型的占位符名称。这个名称可以在函数定义中使用。
      #include <iostream>
      #include <string>
      
      using namespace std;
      
      template <typename T>
      inline T const& Max (T const& a, T const& b) 
      { 
          return a < b ? b:a; 
      } 
      int main ()
      {
       
          int i = 39;
          int j = 20;
          cout << "Max(i, j): " << Max(i, j) << endl; 
          
          double f1 = 13.5; 
          double f2 = 20.7; 
          cout << "Max(f1, f2): " << Max(f1, f2) << endl; 
          
          string s1 = "Hello"; 
          string s2 = "World"; 
          cout << "Max(s1, s2): " << Max(s1, s2) << endl; 
          
          return 0;
      }
      
    4. 类模板

      template <class type> class class-name {
      
      }
      
      • 在这里,type 是占位符类型名称,可以在类被实例化的时候进行指定。您可以使用一个逗号分隔的列表来定义多个泛型数据类型。
      #include <iostream>
      #include <vector>
      #include <cstdlib>
      #include <string>
      #include <stdexcept>
      
      using namespace std;
      
      template <class T>
      class Stack { 
          private: 
              vector<T> elems;     // 元素 
      
          public: 
          void push(T const&);  // 入栈
          void pop();               // 出栈
          T top() const;            // 返回栈顶元素
          bool empty() const{       // 如果为空则返回真。
              return elems.empty(); 
          } 
      }; 
      
      template <class T>
      void Stack<T>::push (T const& elem) 
      { 
          // 追加传入元素的副本
          elems.push_back(elem);    
      } 
      
      template <class T>
      void Stack<T>::pop () 
      { 
          if (elems.empty()) { 
              throw out_of_range("Stack<>::pop(): empty stack"); 
          }
          // 删除最后一个元素
          elems.pop_back();         
      } 
      
      template <class T>
      T Stack<T>::top () const 
      { 
          if (elems.empty()) { 
              throw out_of_range("Stack<>::top(): empty stack"); 
          }
          // 返回最后一个元素的副本 
          return elems.back();      
      } 
      
      int main() 
      { 
          try { 
              Stack<int> intStack;  // int 类型的栈 
              Stack<string> stringStack;    // string 类型的栈 
      
              // 操作 int 类型的栈 
              intStack.push(7); 
              cout << intStack.top() <<endl; 
      
              // 操作 string 类型的栈 
              stringStack.push("hello"); 
              cout << stringStack.top() << std::endl; 
              stringStack.pop(); 
              stringStack.pop(); 
          } 
          catch (exception const& ex) { 
              cerr << "Exception: " << ex.what() <<endl; 
              return -1;
          } 
      }
      

    预处理器

    1. #define 预处理

      • define 预处理指令用于创建符号常量。该符号常量通常称为宏。

        #define macro-name replacement-text 
        
    2. 函数宏

      • 使用 #define 来定义一个带有参数的宏:

        #define MIN(a,b) (((a)<(b)) ? a : b)
        
    3. 条件编译

      • 有几个指令可以用来有选择地对部分程序源代码进行编译。这个过程被称为条件编译。

      • ifdef & endif

        #ifndef NULL
           #define NULL 0
        #endif
        
      • DEBUG 模式

        #ifdef DEBUG
            cerr <<"Variable x = " << x << endl;
        #endif
        
      • 可以使用 #if 0 语句注释掉程序的一部分。

        #if 0
            不进行编译的代码
        #endif
        
    4. # 和 ## 运算符

      • 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。

        #include <iostream>
        using namespace std;
        #define MKSTR( x ) #x
        
        int main ()
        {
            cout << MKSTR(HELLO C++) << endl;
            return 0;
        }
        
      • 运算符用于连接两个令牌。

        #include <iostream>
        using namespace std;
        #define concat(a, b) a ## b
        int main()
        {
            int xy = 100;   
            cout << concat(x, y);
            return 0;
        }
        
    5. 预定义宏

      描述
      _LINE_ 这会在程序编译时包含当前行号。
      _FILE_ 这会在程序编译时包含当前文件名。
      _DATE_ 这会包含一个形式为 month/day/year 的字符串,它表示把源文件转换为目标代码的日期。
      _TIME_ 这会包含一个形式为 hour:minute:second 的字符串,它表示程序被编译的时间。

    信号处理

    1. 信号是由操作系统传给进程的中断,会提早终止一个程序。在 UNIX、LINUX、Mac OS X 或 Windows 系统上,可以通过按 Ctrl+C 产生中断。

    2. 可捕获的信号表(定义在 <csignal> 中)

      信号 描述
      SIGABRT 程序的异常终止,如调用 abort。
      SIGFPE 错误的算术运算,比如除以零或导致溢出的操作。
      SIGILL 检测非法指令。
      SIGINT 接收到交互注意信号。
      SIGSEGV 非法访问内存。
      SIGTERM 发送到程序的终止请求。
    3. signal() 函数

      • 用来捕获突发事件

        void (*signal (int sig, void (*func)(int))(int);
        
      • 接收两个参数:第一个参数是一个整数,代表信号编号;第二个参数是一个指向信号处理函数的指针。

      • 无论要在程序中捕获什么信号,都必须使用 singal 函数来注册信号,并将其与信号处理程序相关联。

        #include <iostream>
        #include <csignal>
        using namespace std;
        void signalHandler( int signum )
        {
            cout << "Interrupt signal (" << signum << ") received.\n";
            // 清理并关闭
            // 终止程序  
            exit(signum);  
        }
        
        int main ()
        {
            // 注册信号 SIGINT 和信号处理程序
            signal(SIGINT, signalHandler);  
            while(1){
                cout << "Going to sleep...." << endl;
                sleep(1);
            }
            return 0;
        }
        
    4. raise() 函数

      • 使用函数 raise() 生成信号。

        int raise (signal sig);
        
      • sig 是要发送的信号编号,这些信号包括:SIGINT SIGABRT SIGFPE SIGILL SIGSEGV SIGTERM SIGHUP

        #include <iostream>
        #include <csignal>
        using namespace std;
        void signalHandler( int signum )
        {
            cout << "Interrupt signal (" << signum << ") received.\n";
            // 清理并关闭
            // 终止程序 
            exit(signum);  
        }
        int main ()
        {
            int i = 0;
            // 注册信号 SIGINT 和信号处理程序
            signal(SIGINT, signalHandler);  
            while(++i){
                cout << "Going to sleep...." << endl;
                if( i == 3 ){
                    raise( SIGINT);
                }
                sleep(1);
            }
            return 0;
        }
        

    相关文章

      网友评论

          本文标题:C++ 快速入门笔记:进阶编程

          本文链接:https://www.haomeiwen.com/subject/wrcouttx.html