• C++Review20_C++函数高级特性(重载、内联、const和virtual)


    重载、内联、const、vitrual:
    const virtual仅用于类的成员函数;

    重载和内联机制可用于全局函数也可用于类的成员函数;

    重载内联有好处,但也有局限性,所以使用时要注意,不能滥用;

    ===============================================

    重载:
    自然语言当中有重载现象,人们可以通过上下文判断该词的真正含义,词的重载可以使得语言更加简练。
    C++程序中,将语义、功能相似的几个函数用同一个名字表示,即函数重载。这样做便于记忆,也提高了函数的易用性。
    当然重载的另一个理由是:类的构造函数需要重载机制;
    因为C++规定构造函数和类同名。构造函数只能有一个名字,如果想用几种不同的方法创建对象,只能用重载机制来实现。
    所以类可以有多个同名的构造函数。

    重名的函数本质上仍然是不同的函数,那么如何进行区分呢?
    靠参数进行区分。编译器根据参数为每个重载函数产生不同的内部标识符。


    这又产生一个问题,C++编译器由于重载的因素,对函数内部标识符的命名和C不一样。
    所以当C++程序要调用已经被编译后的C函数怎么办?
    C++提供了一个C连接交换指定符号extern "c"来解决这个问题。

    extern "C"{
    void foo(int x, int y);
    }
    //这就告诉C++编译器 函数foo是一个C连接,应该到库中找名字_foo而不是找_foo_int_int。
    C++编译器开发商已经对C标准的头文件采取extern C处理,所以我们可以用#include 直接引用这些头文件;

    注意:全局函数和类的成员函数同名不算重载,因为函数的作用域不同。

    但是这里又有个问题:隐式类型转换将导致重载函数产生二义性;
    void output(int x);
    void output(float x);

    如果调用output(0.5);将导致编译错误。因为编译器不知道应该讲0.5转换为int还是float。
    隐式转换可能会简化程序的书写,但是也可能留下隐患;


    成员函数的重载、覆盖、隐藏也很容易混淆;
    重载与覆盖:
    成员函数被重载的特征;
    1)相同的范围(在同一个类中)
    2)函数名字相同;
    3)参数不同;
    4)virtual关键字可有可无;
    覆盖是指派生类函数覆盖基类函数:
    1)不同的范围(分别位于派生类与基类中)
    2)函数名字相同;
    3)参数相同;
    4)基类函数必须有virtual关键字;


    隐藏和覆盖很类似,容易搞混:
    如果派生类的函数与基类的函数同名,但是参数不同。此时无论有无virtual。基类函数都将被隐藏;
    如果派生类函数与基类函数同名,并且参数也相同,但是没有virtual关键字,此时,基类的函数被隐藏;

    其实隐藏和覆盖也很好区分。覆盖的要求比较苛刻,函数名,参数,基类必须有virtual ->其实就是多态;

    隐藏机制会引起不必要的麻烦,有时候想调用基类的函数,但是由于继承的原因被隐藏了,就会出现编译错误;

    参数的缺省值:
    注意缺省值只能在函数的声明中,而不能在定义体内;
    void Foo(int x=0, int y=0); //正确

    void Foo(int x = 0; int y=0){ //错误
    ...
    }

    运算符重载:
    在C++中,可以用关键字operator加上运算符来表示函数,叫做运算符重载。
    Complex operator +(const Complex &a, cosnt Complex &b);
    对于普通函数,函数调用时,参数出现在圆括号内;
    对于运算符重载而言,参数出现在其左、右两侧;
    例如c = a + b;

    如果运算符被重载为全局函数,那么只有一个参数的运算符叫做一元运算符,有两个参数的运算符叫做二元运算符;
    如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个右侧参数。因为对象自己是左侧参数;
    由于C++支持重载,才能把运算符当做函数来用。

    有一些运算符是不能被重载的:. # @ $等
    1、难以理解
    2、难以确定优先级
    3、.对于任何成员都有意义,已经成为标准用法;
    =================================================
    内联:
    内联的目的是为了提高程序的执行效率;
    C语言中可以用宏提高执行效率;

    提高执行效率原因在于
    省去了函数调用,省去了压栈,生成汇编语言call调用,返回参数,执行return等过程;
    从而提高了速度。

    宏代码的局限性:

    使用宏代码最大的缺点是容易出错。运算符优先级方面会出错
    #define MAX(a,b) (a)>(b> ? (a):(b)
    result = MAX(i,j)+2;
    会被处理为:result = (i)>(j) ? (i):(j) +2; //由于+优先级比:高,
    所以上述语句并不等价于期望的 result = ((i)>(j)?(i):(j))+2;

    使用宏代码还有一个缺点是:无法操作类的私有数据成员


    函数内联的工作原理:
    对于任何内联函数,编译器在符号表里放入函数的声明(包括名字,参数类型,返回值类型)。
    如果没有发现内联函数出错,则将函数代码也放进符号表中。
    之后在调用内联函数时,编译器首先检查是否调用正确。
    如果正确,内联函数的代码就会直接替换函数调用,于是直接省去了函数调用的开销。

    这个过程与预处理有显著不同,因为预处理不能进行类型安全检查,自动类型转换,
    假如内联函数是成员函数,对象的地址(this)会被放在合适的地方,这也是预处理器办不到的;

    assert是宏不是函数,是Debug版本起作用的宏,用于检查“不应该”发送的情况;

    内联函数的编程风格:
    必须与定义体放一起才有作用;
    跟声明体放一起不起作用;//这体现了一个基本原则:声明与定义不可混为一谈;

    内联的使用场景:
    1、内联能提高函数的执行效率,但是不能把所有函数都定义成内联;
    2、内联的局限性在于以膨胀代码为代价,省去了函数调用的开销,从而提高函数执行效率。
    3、如果函数体本身的执行时间相对于函数调用的开销较大,那么效率的收益不大。
    4、如果函数体代码较长,将导致内存消耗较高。
    5、所以内联适用于简单的函数,这样内存消耗比较小。

  • 相关阅读:
    面向对象编程-介绍(python3入门)
    课堂作业03
    软件工程个人作业04
    团队介绍
    学习进度条
    课堂作业02
    学习进度条
    软件工程个人作业03
    课堂作业01
    软件工程个人作业02
  • 原文地址:https://www.cnblogs.com/grooovvve/p/12382073.html
Copyright © 2020-2023  润新知