拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 c++编程笔记

c++编程笔记

白鹭 - 2022-01-24 1947 0 0

c++ programming notes

  1. 除错技巧与基本知识

    • GCC 基本知识
      gcc 与 g++ 分别是 GNU 的 c & c++ 编译器 gcc/g++
      在执行编译作业的时候,总共需要4步:

      • preprocessing(编译预处理)

      • compilation(编译)

      #compiles and assembles files but doesn’t link them.
      $ g++ -c
      #This is useful when building large projects to separate file compilation and minimize what is re-compiled.
      
      • assembly(组装)

      • linking(链接)

      • a quick start

      #Say you have a file helloworld.C as follows :
      # #include <stdio.h>
      #     int main ()
      #     {
      #         printf("Hello World\n");
      #     }
      #You can compile and run it from the unix prompt as follows :
      $ g++ helloworld.c
      #This creates an executable called "a.out". You can run it by typing
      $ ./a.out
      #Since no executable name was specified to g++, a.out is chosen by default. Use the "-o" option to change the name :
      $ g++ -o helloworld helloworld.c
      #creates an executable called "helloworld". 
      #using && to execute two commands one by one
      $ g++ debug.cpp -o debug && ./debug
      #notice that you can't type "debug" cause it's not a command you must type "./debug" without g++ before it
      #creates an executable called "debug" and then run it
      

      一些必备指令:
      $ g++ debug.cpp -Wall -Wextra && ./debug | head -100

    • shell基本知识

      • 复制粘贴:如果你按下滑鼠左键,沿着文本拖动鼠标(或者双击一个单词)高亮了一些文本,那么这些高亮的文本就被拷贝到了一个由 X 视窗系统(使 GUI作业的底层引擎)管理的缓冲区里面,然后按下滑鼠右键,这些文本就被粘贴到游标所在的位置,

      • 文件名和命令名是大小写敏感的,

      • Linux 没有“档案扩展名”的概念,不像其它一些系统,可以用你喜欢的任何名字来给档案起名,

      • 大多数命令使用的选项,是由一个中划线加上一个字符组成,例如,“-l”,但是许多命令,包括来自于 GNU 项目的命令,也支持长选项,长选项由两个中划线加上一个字组成,

      • 注意表示法:在描述一个命令时,当有三个圆点跟在一个命令的自变量后面,这意味着那个自变量可以重复

      • ls (list content)

      • pwd (path work directory)

      • cd (change directory) 符号 “.” 指的是作业目录, ”..” 指的是作业目录的父目录,

        • cd .. (回传父目录)
        • cd ./ (= cd)
        • cd 更改作业目录到你的home dictionary(家目录)
        • / 根目录
          • /bin 包含系统启动和运行所必须的二进制程序
        • ~ 家目录
        • /tab/ (auto fill up filename)
      • sudo (Super User Do)

      • file filename 查看档案型别

      • less filename 查看档案

      • An AND list has the form

        #command2 is executed iff command1 returns an exit status of zero (success).
        $ <command1> && <command2>
        
    • VS Code 基本知识
      VS Code 组织结构:全域——作业区(workspace)——档案夹(即项目)

    • wget

    • prinf()的使用

  2. 阵列传递:由于形式自变量和实际自变量是同一阵列(阵列名代表了阵列在存储器中的起始地址(是一个指标),将阵列作为函式自变量时,相当于把实参的阵列的起始地址赋给了形参的阵列的起始地址,因此这两个阵列实际上就是同一个阵列,详见P 120)定义的时候要加中括号,但是形式自变量和实际自变量都是阵列名,所以呼叫这个陈述句时不用加中括号!!!
    注意二维阵列传递的时候需要指定第二维的个数(原因是为了告诉函式每个指标间相差几个存盘空间)

    void swap(char str[], int k, int i)
    //使用方法:swap(string, 3, 1)
    
  3. :系统为函式分配的一块存储器空间,用于存放形式自变量和函式体内定义的变量,函式运行结束时,系统识训分配给该函式的帧,因此存放在这块帧中的变量都消失了,

  4. 堆栈(Stack):函式定义的变量(包括形式自变量)都创建在一个称为堆栈的独立存储器区域,呼叫一个函式相当于从堆栈中分配一个新的帧,并且放在其他活动函式的帧上面return之后,函式对应的帧被识训,帧中的变量就完全消失了,其他函式无法再参考,因此称为区域变量

  5. 变量的作用域main函式中定义的变量也是区域的;一个程序块中定义的变量在本程序块中有效;区域程序块是指一对大括号{}之间的一段C语言程序,如果区域变量与区域程序块以外的变量重名,则前者优先于后者,回到程序块以外时,程序重新进入最初定义的变量的作用范围;变量定义出现在所有函式定义之外,称为全域变量,保存在一个独立存储器区域中,永远都不会被包含区域变量的帧覆写,在区域变量与全域变量同名时,可以使用作用域运算子::加在变量名前,用于指代全域变量;

  6. 变量的存盘类别:变量的存盘位置

    存盘类别 资料型别 变量名表
    //自动变量自动存于stack中,空间也自动被回收
    //c++ 11后不用写auto,只要写 int a, b;
    auto int a, b;
    //自动型别推断,au_a为int型别
    int a = 10;
    auto au_a = a;
    //静态变量,限定变量只能在某个区域使用,放在全域变量区
    //1.静态全域变量(通常是为了防止其他档案参考修改该变量值造成混乱)
    static int x; //全域变量x为当前源档案私有的,其他源档案中不可参考它
    //2.静态区域变量(下次函式呼叫可以使用上次函式呼叫时的值)
    //注1:未初始化的static variable都自动初始化为0,且初值是编译时赋的,运行时不会初始化
    //注2:函式呼叫结束后静态区域变量仍然存在,但其它函式无法参考
    //注3:静态区域变量在程序结束时消亡,先消亡静态区域变量再消亡全域变量
    static int c = 3;//函式在第二次呼叫及以后时该陈述句被忽略,继续使用上次函式呼叫时c的空间
    c = c + 1;
    //暂存器变量(仅限区域自动变量可放入CPU的暂存器中,加快读取速度)
    //仅代表程序员的意向,若无合适暂存器则设为自动变量
    //现在的编译器自动优化
    register int x;
    //外部变量的申明(不是定义不会分配空间,如同函式原型申明)
    /*在全域变量定义之前的陈述句或函式或者另一个源档案中的函式也要使用该全域变量,则在参考之前应该用extern进行外部变量申明,在一个源档案中参考的别的源档案的全域变量就叫外部变量*/
    /*使用外部变量要谨慎,因为修改该变量的值可能会影响另一个源档案中函式执行的结果!!通常用static把某全域变量申明为本档案私有的来防止被其他源档案修改*/
    extern 型别名 函式名
    
  7. 驼峰命名法

  8. 条件断点函式断点

  9. 宏除错:

    #define DEBUG
    #ifdef DEBUG
    #endif DEBUG
    
  10. 算法复杂性
    输入大小
    代码质量
    硬件
    算法复杂性

  11. 时间复杂性
    基本操作
    最坏和平均情况O
    最好情况\(\Omega\)
    最好情况与最坏情况相同\(\Theta\)

  12. 空间复杂性
    S(n)

  13. 排序
    冒泡排序O(n2)
    插入排序O(n2)
    希尔排序(插入的优化)
    计数排序

  14. 参考传递的swap

    template <typename T>
    void swap(T &a, T &b)
    {
        T c = a;
        a = b;
        b = c;
    }
    
  15. 最优化问题
    穷举法
    贪心法:最速下降法(区域最优);硬币找零

  16. 空陈述句

  17. 阵列
    同类,有序,元素个数必须是常量
    定义常量
    阵列名存放了阵列的起始地址,除字符串以外只能一个一个输入输出
    阵列定义在一块连续的空间上

    a[i]
    a + i //这个指标相当于a之后a的型别所定义的第i个存储器区域
    

    C/C++不检查阵列越界!!

  18. 随机数

    strand(time(NULL))
    
  19. 资料的内部视图和外部视图

  20. 指标的概念
    指标的型别决定了编译器解释地址中内容的方式
    *在语法上属于变量名,不属于型别名
    直接访问间接访问
    地址运算子 &x 回传的是变量 x 的地址,不能跟常量或表达式
    *intp 回传 intp 指向的这个变量的内容
    在对 intp 使用参考运算之前,必须先对 intp 赋值
    指标+1表示阵列中指标指向元素的下一元素地址
    可用*(intarray + k)参考intarray[k]
    同类的指标变量之间可相互赋值,表示两个指标指向同一存储器空间

    //NULL是一个特殊指标值,称为空指标,它的值为0,它可被用来初始化一个指标,表示不指向任何地址,
    //c++11引入了nullptr充当单独的空指标常量,与任何指标型别可以发生隐式型别转换,也可以隐式转换为bool型(值为false)但是不存在到整型的隐式型别转换
    void f(int *);
    void f(int);
    //呼叫f(nullptr)将会呼叫函式f(int *)而不会像f(NULL)一样呼叫f(int)
    

    阵列名是一个指标常量
    void *指标变量名;为统配指标型别,可以与任何型别指标相互赋值

  21. **指标与 const **:

    //指向常量的指标(为防止指向的变量被修改而设计)
    const int *p = &x;
    //注意&后面只能跟变量,而且*p不能修改,但是p可以修改如p = &y;
    //指标常量(指向的地址固定但指向的变量的值可以变)定义时必须赋初值
    int *const p = &x;
    //*p可以修改如*P = 20;,但是p不能修改
    //指向常量的指标常量
    const int *const p = &x;
    //不能修改其指向的地址,也不能通过它修改变量的值
    
  22. 动态存储器分配
    对于一个指标变量p
    申请动态变量:p = new int;
    申请动态阵列:p = new int[x];
    注意此处的 x 可以是变量
    申请动态变量并初始化:int *p = new int(10)
    释放动态变量:delete p;
    释放动态阵列:delete [] p; (字符阵列可以不加方括号)
    动态分配:在程序执行程序中需要新的存盘空间时,可用动态分配的方法向系统申请新的空间,当不再使用时用显式的方法还给系统,这部分空间是从被称为堆(Heap)的存储器区域分配,
    注意c++程序运行期间,动态变量不会消亡,必须用delete洗掉

  23. 例外
    除0;空间不够;有些例外可以捕获做处理让程序运行下去

  24. 存储器泄漏
    动态变量是通过指标间接访问的,如果该指标被修改,这个区域就被丢失了,堆管理器认为你在继续使用它们,但你不知道它们在哪里,这称为存储器泄露,
    当释放了存储器区域,堆管理器重新识训这些区域,而指标仍然指向堆区域,但不能再使用指标指向的这些区域,
    释放存储器对一些程序不重要,但对有些程序很重要,如果你的程序要运行很长时间,而且存在存储器泄漏,这样程序会耗尽所有存储器,直至崩溃,

  25. assert宏

    //检查new操作回传值来检验动态空间是否申请成功
    int *p;
    p = new int;
    if (!p) {
    	cout << "allocation failure\n";
    	return 1;
    }
    //用assert宏直接退出程序 #include <cassert>
    int *p;
    p = new int;
    assert(p != 0);//不加这一条后面一句陈述句就可能报错,如果堆空间不够了
    *p = 20;
    
  26. 初始化

    //auto与动态存储器分配
    auto p1 = new auto(10); //注意这里p1前不用加*号
    //c++11 动态阵列初始化
    int *p = new int[5]{1, 2, 3, 4, 5}; //若给出初值个数少于阵列元素个数剩余赋值为0
    
  27. 指标传递输出自变量

    void SolveQuadratic(double a, double b, double c, double *px1, double *px2);
    //传入的只有x1与x2的地址,于是x1与x2可以成为函式的输出,称为输出自变量,设计函式原型时一般将输入自变量放在前面,把输出自变量放在后面
    SolveQuadratic(a, b, c, &x1, &x2);
    cout << x1 << x2;
    
  28. 字符串作为函式的自变量
    如果只读取字符串的话应该使用指向常量的字符指标int word_cnt(const char *s)
    在函式体中可以通过++s的方法遍历字符串

  29. 回传指标的函式:只需在函式名前加一个*号
    注意回传地址对应的变量不能是被调函式的区域变量,否则会消亡
    如果回传的指标是在函式体内new出来的,不要忘记在main函式中使用完后再delete
    char *subString(char *s, int start, int end);

  30. 参考型别:(实际上是一种隐式指标,但每次不用写*号,简便了程序书写)

    coint i;
    int &j = i; //必须赋初值,将j与i的地址关联起来
    int &j1 = j; //valid
    //参考的系结是永久的,不可修改
    //常量参考变量,只能参考,不能赋值
    const int &a = 1; //valid a实际上与一个值为1的临时变量系结了
    int b;
    const int &a = b; //valid 但是不能通过a修改b的值
    //c++11拓展:范围for陈述句,让k以此等于阵列a的每一个元素
    for (int k : a) cout << k; //valid
    for (int k : a) cin >> k; //invalid 这里是值传递
    for (int &k : a) cin >> k; //valid
    //参考传参
    void swap(int &a, int &b); //注意实参必须为左值表达式
    //参考传参的好处之一:节省空间
    int max(const int &a, const int &b); //给函式传入一个常量,防止修改
    //回传参考的函式(可将函式呼叫作为左值)
    int &index(int j)
    {return a[j];} //注意只能参考左值
    void main()
    {
        index(2) = 25; //相当于先执行&index(2) = a[2];,然后index(2) = 25;
        cout << index(2);
    }
    //如果不希望参考回传值通过参考被修改,可申明为const
    const int &index(int j);
    //注意不能回传该函式区域变量的参考
    
  31. 指标阵列
    main函式的自变量

    int main(int argc, char *argv[])
    //argc代表命令列中自变量个数,每个实参都表示为一个字符串,组成一个储存一组字符串的指标阵列*argv[]
    {
        return 0;
    }
    
  32. 指向函式的指标: P 177

  33. 函式指标作为函式自变量

    template<class T>
    void sort(T a[], int size, bool (*f) (T, T));//*f为一个比较函式的指标
    sort(a, 10, increaseInt);//函式名作为第三个自变量
    sort<int>(a, 10, increaseInt);//防止编译器无法确定第三个自变量中T的型别
    //选单选择P180
    //Lambda表达式
    
  34. 多维阵列
    按行存盘
    除错方法:

    • 初始化串列
    • 输入输出重定向
  35. 多级指标

  36. 字符串
    结束标记
    空字符串""占用一个空间存盘\0
    输入输出:

    • 可以直接cin以空格回车或Tab结束
      自学实验指导的实验七
      (注意缓冲区残留的回车键,可用cin.get处理)
    • cout
    • cin.get
    • 推荐cin.getline(字符阵列, 阵列长度, 结束标记)注意cin.getline从键盘读取到阵列长度-1,默认结束标记为回车,这样就可以自由输入空格啦!
  37. cstring:
    strlen(s):回传s的长度,不含'\0',故等于实际长度-1

  38. 函式
    函式宣告:自变量表中的每个自变量说明可以是型别,也可以是型别后面再接一个自变量名,如:

      ```c++
      int max(int, int);
      int max(int a, int b);
      ```
    

    函式执行程序:现场保护

  39. 带默认值的函式:一旦某个自变量指定了默认值,它后面所有自变量都必须有默认值

    void f(int a, int b = 1, int c, int d = 2);
    //该申明是错误的
    

    最好在函式申明时指定默认值,因为自变量默认值是给呼叫者使用的,而编译器是根据函式原型申明来确定函式呼叫是否合法的,故在定义时指定默认值没有意义,除非还充当了函式申明的作用
    不同源档案中的函式申明可以指定不同的默认值,但同一个源档案中只能指定一个默认值,

  40. 行内函式:用于解决呼叫较小函式时进行函式呼叫的额外开销(如分配存储器和回收存储器)有点得不偿失的问题

    inline void function()
    {
        //definition
    }
    

    编译器会将行内函式的代码复制到呼叫处来避免函式呼叫,代价是会使代码变长,
    注意行内函式必须定义在被呼叫之前,因此必须放在头档案中(猜测因为可能会被其它函式呼叫,所以得先定义好编译器才知道复制的代码是是什么),否则编译器不知道应该插入什么代码,
    行内函式只是对编译器的建议,编译器可以根据实际情况决定处理方式

  41. 多载函式:共享函式名
    编译器需要系结:为多载函式中的每一个取不同的内部名字

  42. 函式模板

    template <class T> //T为模板的形式自变量,注意每个形参前都有关键字class或typename
    

    模板的实体化:函式模板实体化形成的函式叫模板函式
    如果模板的形参没有在函式的形参表中至少出现一次,需要显式指定模板实参,因为编译器不会自动推断回传值的型别

    template <class T1, class T2, class T3>
    T3 calc(T1 x, T2 y)
    {return x + y;}
    //呼叫程序:
    calc<int, char, char>(5, a);
    //结果为'f'
    
  43. 包装函式:给用户用的函式

  44. 型别推断

    auto ch = 'A'; //auto定义变量必须赋初值
    auto a = 5, b = 5.5, c = 'A'; //错误,因为同一个auto序列中 变量必须推导为同一型别
    int a, b;
    //不想用表达式值作为初值的型别推断:declaretype(表达式)
    decltype (a + b) c; //编译器不计算a + b的值,而是先推出a + b的型别再把其作为c的型别,c的初值随机
    
  45. 型别别名

    typedef long long ll;
    using REAL = double; //c++11
    //占用空间:sizeof运算子
    sizeof(int);
    sizeof('a' + 15);
    sizeof(x);
    
  46. 常量

    1. 整型常量:

      const long long n = 100; //n = 2LL;
      0123 //八进制,Octal,缩写OCT或O
      0x7fffffff //十六进制,Hexadecimal,简写为hex
      
    2. 实型常量:

      指数e尾数
      指数E尾数
      1e3.3 //invalid
      e3 //invalid
      e //invalid
      -18.234e-10 //valid
      //一般视为double,若要视为float: 1.23f 或 1.23F
      
    3. 字符常量:

      \n //换行:移到下一行开始,编码为10
      \r //回车:回到当前行开始,编码为13
      \t //Tab(水平制表符):水平移到下一个Tab区,编码为9
      \0 //空字符
      \177 //ASCLL值为OCT177对应字符,即为decimal127对应字符Delete键编码
      \xhh //两位十六进制编码
      
    4. 布尔常量:true false

    5. 符号常量:
      通常全用用大写字母

      #define PI 3.14159 //c语言定义的符号常量称为宏,用编译预处理指令#define来定义
      #define RADIUS 3 + 5 //宏定义属于简单字符串替换,不加括号会出问题!!
      const double PI = 3.14159 /*只能在定义时被赋值,不能修改,右边表达式是任意的,而且初值可以不是编译时能确定的*/
      /*c++程序中某些值必须是编译时的常量,即常量表达式,如阵列元素个数,为此引入constexpr来定义const expression*/
      constexpr int N = 10;
      //只有N申明为constexpr时编译器定义M时才会被通知N为常量,若N为const或者变量则会报错
      constexpr int M = N + 10; 
      /*普通函式呼叫出现在const expression中编译器会报错,因为编译器不会自动检查函式呼叫结果是否为编译时常量,为此引入constexpr函式,当constexpr函式出现在const expression中时,编译器会检查本次呼叫结果是否为常量,若是,通过编译,否则报错,注意只要求结果是否为常量,不在意常量如何得到,定义时关键字constexpr放在函式头最前面*/
      //valid
      constexpr int f1() {return 10;}
      constexpr int x = 1 + f1(); 
      //invalid
      int f1() {return 10;}
      constexpr int x = 1 + f1();
      //constexpr函式的返回传值可以不是常量,只要不用于const expression中就行,否则会报错
      /*constexpr函式中只能有一个执行实际操作的陈述句:唯一一条return陈述句,不允许有其他执行操作的陈述句,可以有型别别名,空陈述句等,编译时编译器会将函式呼叫替换成函式的回传值,因此必须在编译时进入函式而不发生函式呼叫,因此constexpr函式一定会被隐式指定为inline函式,编译时会将代码复制过来运行,因此也必须放到头档案里(其它函式可能会呼叫,因此必须预先定义)*/
      constexpr int f2(int n) {return (n % 2) ? n + 10 : 11;} //valid
      constexpr int f2(int n) {if (n % 2) return n + 10 else return 11;} //invalid
      
  47. 尾置回传型别:回传值型别取决于呼叫时实际自变量型别时,为了让编译器自动推导,而不用显示指定形参模板,c++11引入了新的函式申明语法:尾置回传型别

    template<class Type1, class Type2>
    auto cal(Type1 alpha, Type2 beta)->decltype(alpha + beta)
    {return alpha + beta;}
    //以下invalid,因为编译器无法在编译时确定alpha + beta的型别(因为还未定义)
    template<class Type1, class Type2>
    decltype(alpha + beta) cal(Type1 alpha, Type2 beta)
    {return alpha + beta;}
    
  48. 排序进阶
    求单调函式反函式
    堆:红色第四章、第五章

  49. toupper(ch):小写转大写,大写不变
    #include <ctype.h>

  50. 目标代码:object code: In computing, object code or object module is the product of a compiler. Object files can in turn be linked to form an executable file or library file. An assembler is used to convert assembly code into machine code (object code). A linker links several object (and library) files to generate an executable. Assemblers can also assemble directly to machine code executable files without the object intermediary step.

  51. priority queue

  52. Encapsulation(封装):the bundling of data with the methods that operate on that data, or the restricting of direct access to some of an object's components
    Publicly accessible methods are generally provided in the class to access or modify the state more abstractly. In practice sometimes methods (so-called "getters" and "setters") are provided to access the values indirectly, but, although not necessarily a violation of abstract encapsulation, they are often considered a sign-post of potentially poor object-oriented programming (OOP) design practice

  53. Function prototype(函式原型)/ Function interface(函式界面):a declaration of a function that specifies the function’s name and type signature(型别签名)(arity(变元个数), data types of parameters (formal argument 形式自变量), and return type), but omits the function body

  54. Include directive: In C and C++, the #include preprocessor directive causes the compiler to replace that line with the entire text of the contents of the named source file (if included in quotes: "") or named header (if included in angle brackets: <>), note that a header doesn't need to be a source file
    Headers need not have names corresponding to files: in C++ standard headers are typically identified with words, like "vector", hence #include <vector> while in C standard headers have identifiers in the form of filenames with a ".h" extension, as in #include <stdio.h>. A "source file" can be any file, with a name of any form, but is most commonly named with a ".h" extension and called a "header file" (sometimes ".hpp" or ".hh" to distinguish C++ headers), though files with .c, .cc, and .cpp extensions may also be include.
    In practice, what is usually done is that the angle-brackets form searches for source files in a standard system directory (or set of directories), and then searches for source files in local or project-specific paths (specified on the command line, in an environment variable, or in a Makefile or other build file), while the form with quotes does not search in a standard system directory, only searching in local or project-specific paths. In case there is no clash, the angle-brackets form can also be used to specify project-specific includes, but this is considered poor form. The fact that headers need not correspond to files is primarily an implementation technicality, and used to omit the .h extension in including C++ standard headers; in common use "header" means "header file".

  55. size_t: size_t的取值range是目标平台下最大可能的阵列尺寸,能增加代码可移植性,

  56. int补码表示:按位取反加一

  57. 物件this指标

    //此处的this指标可省略(物件作为整体访问时要显式地使用this指标,即*this)
    void create(int n, int d) {this->num = n; this->den = d; this->ReductFraction();}
    
  58. 构造

    • 建构式的名字与类相同,是类的成员函式

    • 定义物件时自动呼叫,不能指定回传型别

    • 建构式可以(共享函式名),定义物件时根据初值选择相应的建构式

    • 定义时必须给出实际自变量表类名 物件名(实际自变量表);
      除非有一个建构式没有自变量才可用类名 物件名();

    • 不带自变量的建构式称为默认建构式,一个类通常只有一个默认建构式

    • 编译器自动生成的建构式没有自变量,函式体为空,此时生成物件的所有资料成员初值为随机值

    • 写了建构式就一定要有对应的实参,因为编译器不再生成空的建构式了

    • 指定默认值可以解决不想写默认建构式的麻烦

    • 初始化串列

      DoubleArray::DoubleArray(int lh, int rh):low(lh), high(rh)
      {
      	storage = new double [high - low + 1];
      }
      
    • 初始化串列的好处:使资料成员的初始化和赋初值同步进行,提高了函式的效率, 否则系统会先执行每个资料成员对应类的默认建构式,再执行整个类的默认建构式

    • 必须用初始化串列的场合:

      • 资料成员是某个类的物件,不能直接用赋值陈述句赋初值
      • 类包含一个常量的资料成员
    • 初始化次序是按照定义时的次序来的

    • 拷贝建构式:同型别变量初始化

      //虽然我们可以随便写,但是其本意是构造一个一模一样的物件
      Rational(const Rational &obj) //不能写成值传递,否则会自我无限呼叫而不终止
      {num = 2 * obj.num; den = obj.den;}
      Rational r3 = r1;
      
    • 默认拷贝建构式的缺点:如果有指标的话会指向同一块空间

    • 呼叫拷贝建构式

      //物件定义时
      DoubleArray array2(array1);
      DoubleArray array2 = array1;
      //函式呼叫时:值传递
      //函式回传时:回传值等于一个临时物件
      
  59. 析构
    ~DoubleArray(){if (storage) delete [] storage;}无自变量无回传值

  60. 生成默认建构式:
    CreateAndDestroy() = default;

  61. 阻止拷贝
    BoubleArray(const DoubleArray &) = delete;

  62. 委托建构式

  63. 初始化串列

  64. 类内初始化

  65. const与类

    • 常量函式成员
      const int size; //只能在建构式的初始化串列中完成
    • 常量物件
      const Rational r1(1, 3); //只能用建构式初始化不能使用赋值陈述句
    • 常量成员函式
      不改变资料成员的函式都必须宣告为const,在函式头后加一个保留字const
      常量物件只能呼叫常量成员函式
  66. 静态成员

    • 静态资料成员static double rate;
      类的静态成员函式拥有一块单独的存盘区,只在类的范围内有效,可以是公有或私有
      定义在类的实作档案中
      double SavingAccount::rate = 0.05; //类的空间只有在定义时分配,必须单独在类的实作档案中定义,才能分配空间
      可以通过类名直接呼叫,如:类名::静态资料成员名;或和普通成员一样呼叫

    • 静态成员函式
      为类服务,而不是类的物件
      可以写在类定义中

      static void SetRate(double newRate);
      

      也可以写在类定义外面,不加static
      可以通过类名直接呼叫,如:类名::静态成员函式(实际自变量表);或和普通成员函式一样呼叫
      没有有隐含的this指标,只能访问静态成员

    • 静态常量成员
      整个类共享的常量
      一般而言,类的资料成员不能再类定义时初始化:

      • 普通的资料成员在物件定义时由解构式初始化
      • 静态资料成员在静态资料成员定义时初始化
      • 例外:静态常量资料成员必须在类定义时初始化
  67. 友元
    最好把友元函式的申明放在类定义最前面或最后面,并且在前面不添加任何访问控制说明

    • 友元(全域)函式
      friend void f();
      定义可以写在类定义里
    • 友元成员函式
      friend int B::func(double);
    • 友元类
      friend class B; //class B 中所有成员函式都可以直接访问该类私有成员
  68. 运算子多载:不改变运算物件数,不改变优先级和结合性

    • 多载为全域函式(作为类的友元函式)

      //多载成友元函式,自变量的个数,型别,回传型别与期望的完全相同
      //具有两个运算物件其回传一个新的物件的运算子建议多载为全域函式,如+, -, *, >
      //更加灵活,第一个运算物件不是this指标,可以进行自动型别转换
      friend Complex operater+(const Complex &c1, const Complex &c2)
      {return Complex(c1.real + c2.real, c1.imag + c2.imag);} //这里默认建构式写在后面的public中
      friend Complex operater-(const Complex &c1, const Complex &c2)
      {return Complex(c1.real - c2.real, c1.imag - c2.imag);}
      
    • 函式呼叫运算子多载

    • 多载为成员函式

      //c++规定隐含自变量this是运算子的第一个自变量
      //必须设为公有成员函式,且不改变运算物件值的函式设为const的成员函式
      //=, [], (), ->必须多载成成员函式,这样编译器才能检查第一个运算物件是不是相应的类物件
      //++, --, 
      public:
      	Rational operater+(const Rational &r1) const
          {
              Rational tmp;
              tmp.num = num * r1.den + r1.num * den; //这里的num和den都是隐式添加了this->的
              tmp.den = den * r1.den;
              tmp.ReductFraction();
              return tmp;
          }
      	Rational operater*(const Rational &r1) const;
      //赋值运算子多载
      //c++中赋值运算构成一个表达式,回传值为左边物件本身,可以是左值
      //这样就可以实作用于类似 (a=b)=c 这样的再次对a=b进行写操作的表达式
      DoubleArray &DoubleArray::operater=(const DoubleArray &right)
      {
          if (this == &right) return *this; //防止自己复制自己,否则会把自己delete掉了
      	delete [] storage; //归还空间
          low = right.low;
          high = right.high;
          storage = new double[high - low + 1]; //重新申请空间
          for (int i = 0; i <= high - low; ++i) //复制阵列元素
      		storage[i] = right.storage[i];
      	return *this;
      }
      //一般需要自定义复制建构式的类也需要多载赋值运算子
      Rational r2 = r1; //拷贝建构式
      r1 = r3; //赋值运算子多载函式
      //下标运算子的多载
      //回传对应阵列元素的参考,可以是左值
      double &DoubleArray::operator[](int index)
      {
          if (index < low || index > high) {cout << "下标越界"; exit(-1);}
      	return storage[index - low];
      }
      //回传右值
      const double operator[](int i) const;
      //前缀++
      Rational &Rational::operator++(); //回传的是当前物件,不会消亡
      //后缀++
      //为与前缀区分,接受一个额外无用的int形参,使用后缀时,编译器自动用0作为这个自变量的值
      Rational Rational::operator++(int); //回传的是一个新建的物件,函式结束后消亡,不可参考
      //尽量使用前置
      
  69. 组合
    一个类的某个资料成员是另外一个类的物件,则该物件称为物件成员
    如果类含有物件成员,新类物件初始化时也不想用默认建构式去初始化物件成员,必须用建构式的初始化串列去初始化成员物件,
    Complex(int r1 = 0, int r2 = 1, int i1 = 0, int i2 = 1):real(r1, r2), imag(i1, i2) {}
    Complex(Rational r, Rational i):real(r), imag(i) {}

  70. 继承

  71. 例外处理
    例外抛出时,throw后操作数初始化该型别的一个临时副本,传回呼叫该程序的程序,当前函式终止,区域变量被析构

    • throw myerror(“something bad happened”);

      myerror是一个类,它以字符串变量为自变量

    • throw int(5)
      int是一个内部型别,5是一个int型别的常数

    • // Class DivideByZeroException to be used in exception
      // handling for throwing an exception on a division by zero.
      class DivideByZeroException {
       public:
          //建构式
           DivideByZeroException():message("attempted to divide by zero") {}
           const char *what() const {return message;} //回传一个常量字符阵列(字符串)
       private:
           const char *message; //记录出现的例外情况
       };
      
  72. 容器
    容器是保存和处理一组有某种关系的同类资料的型别,迭代器是一个抽象指标

  73. 堆栈

    • LIFO
    • 对于n个不同的元素,给定入堆栈的顺序,求有多少种出堆栈顺序,
    • 考察最后一个出堆栈的元素是第几个入堆栈的元素
      重要观察:如果最后出堆栈的是a[i],则a[i]入堆栈前a[1]…a[i-1]必然已经出堆栈,这种出堆栈的顺序数为f(i-1)
      剩下a[i+1]…a[n]的出堆栈顺序数为f(n-i)
      卡特兰数:DP复杂度O(n^2)
    • 从另一个角度思考(复杂度O(n))
      令1表示进堆栈,0表示出堆栈,则原问题可转化为求一个2n位、含n个1、n个0的二进制数,满足从左往右扫描到任意一位时,经过的0数不多于1数的个数,
      含n个1、n个0的2n位二进制数共有C(2n,n)个,其中不满足条件的有C(2n,n+1)个,
      原因:不满足条件的数在某一位第一次0的个数大于1的个数,从那以后全部按位取反可以得到一个n+1个0,n-1个1的数,这个数与原来的数一一对应(为什么?我已经想清楚了)
    • 堆栈的链表实作:
      用链表头的指标指向堆栈顶(不能用tail,因为洗掉操作必须在堆栈顶完成)
    • 重要的堆栈
      函式的呼叫和回传:
      在堆栈中记录函式呼叫回传的地址,一层层push进去
      递回呼叫堆栈有深度,不能呼叫太多次
    • 堆栈的应用
      DFS
      括号匹配
  74. 队列

    • FIFO

    • 队列的实作:

      • 队头位置不固定

      • 含空单元的回圈队列

      • 少用一个存盘单元,当顺序存盘空间的容量为maxSize时,只允许最多存盘maxSize-1个资料元素,此时判断空的条件为 front == rear ,判断满的条件是front == (rear+1) % maxSize;

      • 一般记录两个指标,指向队首队尾

    • 队列的应用
      云计算
      BFS
      排队系统
      滑动视窗的最大值

  75. while(cin>>a)
    只要输入的值有效,那么就执行while体内的陈述句,
    (1)若流是有效的,即流未遇到错误,那么检测成功,
    (2)若遇到档案结束符,或遇到一个无效的输入时(例如本题输入的值不是一个整数)istream物件的状态会变为无效,条件就为假,

本文来自博客园,作者:Danny2003,转载请注明原文链接:https://www.cnblogs.com/Danny2003/p/15784406.html

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *