美文网首页
《C++ Primer Plus》:函数探幽

《C++ Primer Plus》:函数探幽

作者: Dragon_boy | 来源:发表于2020-07-28 17:15 被阅读0次

    本章内容概览

    • 内联函数
    • 引用变量
    • 按引用传递函数参数
    • 默认参数
    • 函数重载
    • 函数模板

    内联函数

    内联函数是C++为提高运行速度的改进,和常规函数编写一样,只是编译器组合到程序的方式不同。常规函数调用时,编译器会根据函数地址跳转,执行代码,函数结束后跳转回来,而内联函数是编译器使用相应的函数代码替换函数调用,程序不会跳到另一处执行代码,所以内联函数运行速度快,但相比之下占用内存大。

    要使用内联函数,应在声明和定义前加上inline关键字,不过通常会省略原型。注意,内联函数不能递归。

    如计算平方的函数:

    inline double square(double x) {return x * x;}
    

    引用变量

    引用是已定义的变量的别名。引用变量的主要用途是用作函数的形参,通过将引用变量用作参数,函数将使用原始数据,而不是其副本。

    C++使用&运算符来声明引用:

    int rats;
    int& rodents = rats;
    

    上述引用声明允许将rats和rodents互换,它们指向相同的值和内存单元。

    引用和指针看起来很像,但还是有差别的,其中之一是声明引用时必须初始化:

    int rat;
    int& rodent;
    rodent = rat;  //invalid
    

    引用更像是const指针,都必须在声明时初始化。

    函数可以将引用用作函数参数:

    void swap(int& a, int& b);
    

    调用后,函数体中使用的相当于是真实的值,而不是拷贝值,这和指针,按地址传递参数很像。

    同样,还可以使用const限定符修饰形参,表明不能修改引用:

    void refcube(const double& ra);
    

    如果实参和引用参数不匹配,C++将生成临时变量,如使用表达式作为实参传入。当前,仅当参数为const引用时,C++才能这么做。

    如果引用参数是const,编译器将在下面两种情况下生成临时变量:

    • 实参的类型正确,但不是左值
    • 实参的类型不正确,但可以转换为正确的类型。

    左值是可被引用的数据对象,如变量、数组元素、结构成员、引用和解除引用的指针都是左值,非左值包含字面常量和包含多项式的表达式。但常规变量和const变量都可以视为左值。

    C++11还新增了右值引用,使用&&声明:

    double&& rref = std::sqrt(36.00);
    double j = 15.0;
    double&& jref = 2.0 * j + 18.5;
    

    引用的引入其实是为了方便将结构和类用作参数:

    struct free_throws
    {
        std::string name;
        int made;
        int attempts;
        float percent;
    };
    
    void set_pc(free_throw& ft);
    

    同时返回值可以返回一个引用,如果不是的话,就是一个拷贝:

    free_throw& set_pc(free_throw& ft);
    

    默认参数

    默认参数位于非默认参数后定义,调用函数时,要么不设置默认值,要么全部设置:

    int harpo(int n, int m = 4, int j = 5);
    

    函数重载

    函数重载的关键是函数的参数列表——也成为函数特征标,如果两个函数的参数的数目和类型相同,同时参数的排列顺序相同,那么它们的特征标相同,变量名无关紧要。C++允许定义名称相同的函数,但需要特征标不同:

    void print(const char* str, int width);
    void print(double d, int width);
    void print(long l, int width);
    void print(int i, int width);
    void print(const char* str);
    

    不过这种时候如果传入了不正确的类型作为参数,C++会拒绝进行强制转换。

    同时,下面两个函数原型的特征标相同,所以不是函数重载:

    double cube(double x);
    double cube(double& x);
    

    类型引用和类型本身是同一个特征标。

    同时,匹配函数时并不区分const和非const变量:

    void dribble(char* bits);  // 可以传入const和非const
    void dribble(const char* bits);  //只能传入const
    

    总之注意,函数重载必须是特征标不同,如果要返回类型不同的话,特征标也必须不同。

    函数模板

    比如定义一个交换模板函数:

    template <typename AntType>
    void Swap(AntType& a, AnyType& b)
    {
        AnyType temp;
        temp = a;
        a = b;
        b = temp;
    }
    

    模板并不创建函数,只是告诉编译器如何定义函数。比如:

    int a = 1;
    int b = 2;
    Swap(a,b);
    

    编译器会用int替换AnyTyoe,创建一个int版本的函数。

    模板重载和常规函数重载类似,只要特征标不同即可。

    显式具体化,对于常规模板函数定义,对于某些操作可能无意义,比如,整型可以直接在if语句中比较,但数组不行,同样结构体不能单独交换成员变量的值。

    我们可以这么定义:

    template <typename T>
    void Swap(T&, T&);
    
    template <> void Swap<job> (job&, job&);
    

    job是一个结构体。如果传入非job类型值,则会使用普通版本模板函数,如果是job类型值,就会使用特定的模板。

    实例化和具体化

    在代码中包含函数模板本身并不会生成函数定义,只是一个用于生成函数定义的方案,编译器使用模板为特定类型生成函数定义时,得到的是模板实例,这种实例化方式是隐式实例化,C++还允许显示实例化,比如int类型:

    template void Swap<int>(int, int);
    

    这可以直接让编译器创建特定的实例。

    而显示具体化:

    template <> void Swap<int> (int&, int&);
    

    区别在于<>,同时显示具体化是要求编译器遇到对应参数时使用该模板创建定义,而显示实例化是直接创建对应类型的实例。

    还可以通过使用函数来创建显示实例化:

    template <typename T>
    T Add(T a, T b)
    {
        return a + b;
    }
    ...
    int m = 6;
    double x = 10.2;
    cout << Add<double>(x,m) << endl;
    

    <double>表明将非double参数转换为double参数。不过对于Swap不行:

    int m =5;
    double x = 14.3;
    Swap<double>(m,x);
    

    第一个参数是double&,不能指向int m。

    对于函数重载、函数模板和函数模板重载,C++确定使用哪一个函数定义的策略是重载解析:

    • 创建候选函数列表
    • 使用候选函数列表创建可行函数列表
    • 确定是否有最佳的可行函数

    有时候我们可以自定义引导编译器使用哪种函数,比如可以:

    lesser<>(m,n)
    

    <>表明使用模板函数。

    模板函数发展

    比如下面的模板函数:

    template<typename T1, typename T2>
    void ft(T1 x, T2, y)
    {
        ...
        xpy = x + y;
        ...
    }
    

    我们不知道xpy是什么类型的,因为x和y可以是任何类型。

    C++提供了关键字decltype:

    int x;
    decltype(x) y;
    

    这样,y的类型就和x类型一致,decltype的参数可以是表达式,上述模板函数可以变为:

    template<typename T1, typename T2>
    void ft(T1 x, T2, y)
    {
        ...
        decltype(x + y) xpy = x + y;
        ...
    }
    

    不过decltype不能处理返回语句的值的类型:

    template<typename T1, typename T2>
    ?type? ft(T1 x, T2, y)
    {
        ...
        return x + y;
    }
    

    编译器无法预知x+y的类型,所以无法使用decltype,我们可以使用auto:

    template<typename T1, typename T2>
    auto ft(T1 x, T2, y)
    {
        ...
        return x + y;
    }
    

    相关文章

      网友评论

          本文标题:《C++ Primer Plus》:函数探幽

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