跳至正文
wyaaaattwhoAI RESEARCH & ENGINEERING
← 返回首页

C++对C的基本扩充hred

基本扩充

结构化程序设计:单入口单出口,避免耦合性过高(Chrome nobody cares)
  • 局部变量的定义位置: C++允许局部变量的定义可以夹插在语句中。变量的使用点与定义点相距不至于太远,便于理解与维护。
  • 常量定义:
    • 变量初始化方法:
      • 条件的满足与不满足 0表示不满足,非0表示满足 bool类型:true表示满足;false表示不满足(C++扩充的,便于理解)
      现行C标准下引入<bool.h>也可以使用True/False
      • 内联(inline)函数:编译时把函数体嵌入到函数的调用点,提高对小函数的调用效率。 可以给函数的参数指定默认值:函数调用时一些实参可以缺省,方便使用。
      • 名空间(namespace):使得不同模块可以有同名的全局实体,应对名冲突。
      • 标准库:保留了C的标准库,但头文件名从*.h 改成 c*,另外,库中的全局实体定义在名空间std中。
      • 动态变量的内存分配:增加了新的操作符new与delete,保证类型安全。 引用类型:具有指针的一些作用,但比指针更安全。
      • 函数名重载:在同一个作用域中,多个函数可以取相同的名字。
      static关键词可以声明静态量,改变作用域,生命时间以及共享的类
      • 基于范围的for语句:自动遍历数组元素,防止数组下标越界。
      • 类型自动判断auto:根据初始化的值自动判断变量的类型,方便某些类型变量的定义。
      • 结构化异常处理:把程序的正常处理与异常处理的代码分离开,使得程序容易理解与维护。
      • 匿名函数(λ表达式):把函数定义和使用合而为一,方便对一些临时函数的使用。
      • 输入/输出:保证类型安全

      输入/输出

      再C++中,我们使用cin与cout

      解决小函数低效问题

      C++提供了两种解决方案
      • 带参数的Macro
      • 内联函数
      Macro has a lot of problems, such as repeatedly calculation and too many brackets
      The problem about inline function is that when the function is a recursion function, it would be taken as a normal function; you’re actually offering an suggestion to the IDE, it might take it or not
      💡
      内联函数的作用是建议编译程序把该函数的函数体展开到调用点,函数调用时直接执行函数体。

      Default parameter

      C++ allows you to set a default parameter for your function
      💡
      default parameter has to be on the right side

      Namespace(名空间)

      This feature allows us to use homonymous global variates and functions
      This could also give static a rest, whereas in C we have to use static to limit a variate’s scope
      to one single file, no we can use a nameless namespace to do the same thing
      So now static could be simply used for changing lifecycle
      💡
      这样,static就可以只用于一个目的了:使得局部变量采用静态存储分配。

      Dynamic variate

      动态变量是指在程序运行中,由程序根据需要额外创建的变量,主要用于动态数组和链表等。 In C, we use standard functions like calloc or malloc to create such variates
      C++ offers a better choice
      💡
      一般来说,用new创建的动态变量需要用delete来撤销; 用malloc(calloc)创建的动态变量则需要用free撤销。 对于普通的动态变量,C++与C的做法区别不大,但如果创建的是动态对象,则两者是有差别的:
      • 除了为对象分配空间外,new还会去调用对象类的构造函数进行对象初始化, malloc(calloc)则否。
      • 除了收回为对象分配的空间外,delete还会去调用对象类的析构函数进行对象消亡后的清理,free则否。

      程序实体在内存中的安排

      程序运行时,程序中的实体将存储在四个区域中:
      • 静态数据区:用于全局变量、static存储类的局部变量以及常量的内存分配 。如果没有显式初始化,系统将把它们初始化成0。
      • 代码区:用于存放程序的指令,对C++程序而言,代码区存放的是所有函数代码。
      • 栈区:用于自动存储类的局部变量、函数的形式参数以及函数调用时有关信息(如:函数返回地址等)的内存分配。
      • 堆区:用于动态变量的内存分配。
      notion image

      引用类型

      引用类型是用来给一个变量取一个别名,通过该别名可以访问原来的变量。例如:
      • 引用类型的变量没有自己的内存空间,而是共享它引用的变量的内存空间。
      • 引用类型具有指针类型的一些效果,但语法不一样。例如:
        💡
        引用主要用于函数的参数类型,实现指针类型参数的效果,但比指针类型抽象和安全
        💡
        用引用类型提高函数参数传递的效率,例如:
        • 引用可以保证始终指向实参,而不会出错,例如下:
          • 引用类型可以作为返回值
            • 引用类型一般作为指针类型来实现(有时又把引用类型称作隐蔽的指针,hidden pointer)

            Based for-loop

            字符串常量与字符指针

            按C语言,可以;按C++语言,不可以!不允许把常量赋值给一般指针
            应该考虑常量指针

            类型自动判断auto

            • 在C语言中,关键词auto用来显式指示具有自动生存期的局部变量。
            • 在C++新国际标准(C++11以后)中,关键词auto有新的含义:
              • 定义一个变量时可以不指定它的类型,由编译器根据初始化的值自动确定它的类型。例如: auto x=1+2*3.4;编译程序自动确定x的类型为double
              • 再例如: int f(double x,int y) { ...... } auto fp=f;
                • 编译程序自动确定fp的类型为:int (*fp)(double,int)
                  因此,自动生存期的局部变量不能再显式地用auto来指出了!

            函数名重载

            • 确定一个对重载函数的调用对应着哪一个重载函数定义的过程称为绑定(binding,又称定联、联编、捆绑)。
            • 例如,print(1.0)调用的是下面的哪一个重载函数?
              • void print(int i) { ...... } void print(double d) { ...... } void print(char c) { ...... } void print(A a) { ...... }
                对重载函数调用的绑定一般是在编译时刻由编译程序根据实参与形参的匹配情况来决定。
            • 绑定规则
              • 精确匹配
                • 实参与形参的类型完全相同,或者对实参进行“微小”的类型转换后与形参类型相同: 数组变量名->数组第一个元素的内存地址 函数名->函数的内存首地址 ...... 例如,对于下面的重载函数定义: void print(int); void print(double); void print(char); void print(char *); 下面的函数调用:
                  • print(1); //绑定到函数: void print(int); print(1.0); //绑定到函数:void print(double); print('a'); //绑定到函数:void print(char); char s[10]; print(s); //绑定到函数:void print(char *);
              • 提升匹配
                • 先对实参进行下面的类型提升,然后进行精确匹配:
                • 按整型提升规则提升实参类型 把float类型实参提升到double 把double类型实参提升到long double
                • 例如,对于下述的重载函数: void print(int); void print(double); 根据提升匹配,下面的函数调用: print('a'); 绑定到函数:void print(int); print(1.0f); 绑定到函数:void print(double);
              • 标准转换匹配
                • 先进行下面的标准转换,然后进行精确匹配:
                • 任何算术类型可以互相转换 枚举类型可以转换成任何算术类型 零可以转换成任何算术类型或指针类型 任何类型的指针可以转换成void *
                • 例如,对于下述的重载函数: void print(char); void print(char *); 根据标准转换匹配,下面的函数调用: print(1); 绑定到函数:void print(char);
              • 自定义转换匹配
              • 匹配失败
                • 如果不存在匹配或存在多个匹配,则绑定失败。例如,对于下述的重载函数: void print(char); void print(double);
                • 根据标准转换匹配,下面的函数调用将会绑定失败: print(1); 因为1(属于int型)既可以转成char,又可以转成double
                • 解决办法是:对实参进行显式类型转换,如, print ((char)1) 或 print ((double)1)
                • 增加额外的重载,如, 增加一个重载函数定义: void print(int);