• 第八章 函数幽探


    内联与宏:

    C++新增的内联函数与普通函数一样是按值传递的,而宏是直接替换的。

    #define SQUARE(X) X*X
    int a;
    a = SQUARE(5+10) //相当于a = 5+10*5+10

    C++的引用变量:

    引用变量的最大用途是用作函数的形参,通过将引用变量作为参数,函数将使用原始数据而不是其副本。引用为处理大型结构提供了非常方便的途径。

    将引用作为函数参数的参数传递方法称为按引用传递。

    传递引用的限制更严格,引用只是得到了变量的别名,此时将不能传递表达式。

    临时变量、引用参数和const

     如果实参和引用参数不匹配,C++将生成临时变量。但是目前,仅当参数为const引用时,C++才允许这么做(以前不一样)。

    下面两种情况将生成临时变量:

    1. 参数的类型正确,但不是左值。

    2. 参数的类型不正确但可以转换成正确的类型。

    为什么要生成临时变量?

    如果接受引用参数的函数的意图是修改作为参数传递的变量,那么创建临时变量将阻止这种意图的实现,其行为类似于按值传递;

    如果接受引用参数的函数的目的只是使用传递的值,而不是修改他们,因此临时变量不会造成任何不利的影响,反而会使函数在可处理的参数种类方面更通用。

    应尽可能将引用声明为const:

    1. 使用const可以避免无意中修改数据的编程错误;

    2. 使用const使函数能够处理const和非const实参,否则将只能接受非const数据;

    3. 使用const引用,使函数能够正确生成并使用临时变量。

    正常情况下接受引用参数的函数的实参是左值,若不是左值,但实参类型正确且为const引用的话,将生成临时变量。

    C++11新增了另一种引用,右值引用,是使用&&声明的(把以前的引用称为左值引用)

    double j = 15.0;
    double && jref = 2.0*j + 18.5; //不能使用double &声明jref

    使用引用的主要目的是应用与结构和类的,而不是基本的内置类型。

    将引用用于结构体

    返回引用时,应避免引用函数终止时不再存在的内存单元(例如,返回临时变量的引用)

    函数返回结构体引用时,函数调用可以作为左值被赋值;为避免函数调用作为左值被赋值(防止返回的内存块被修改),一般返回const引用。

    将引用用于对象

    string类定义了一种char* 到string的转换功能,这使得可以使用C-风格字符串来初始化string对象

    基类引用可以指向派生类对象,而无需进行强制类型转换。

    传递类对象的标准方式是按引用传递。

    C++的输出格式化:

     1 #include<iostream>
     2 using namespace std;
     3 int main()
     4 {
     5     ios_base::fmtflags inits;//数据类型ios_base::fmtflags声明的变量用于存储,给此变量赋值前的所有格式化设置
     6     double x = 3333.0;
     7 //    cout.setf(ios_base::fixed);//定点表示法
     8 //    cout.setf(ios_base::showpoint);//将对象置于显示小数点的模式
     9     cout.precision(2);//设置显示精度
    10     inits = cout.width(15);//设置字段宽度
    11     cout << x << endl;
    12     cout.setf(inits);//返回inits之前的所有格式化设置
    13     cout << x << endl;
    14     char str[4] = "abc";
    15     cout << str[3] << endl;
    16     system("pause");
    17     return 0;
    18 }

    默认参数

    函数重载:

    默认参数让程序员能够使用不同数目的参数调用同一个函数,而函数多态(重载)使能够使用多个重名的函数。

    函数重载的关键是函数的参数列表——也称函数特征标(function signature)。

    C++允许定义名称相同的函数,条件是它们的特征标不同(返回类型可以相同也可以不同)。

    如果参数类型和/或参数数目不同,则特征标也不同。

    C++在检查特征标时,将类型和类型引用视为同一特征标,而不会将const和非const变量视为同一特征标。

    若一个函数有多个重载版本的话,将调用最匹配的版本。

    名称修饰:

    C++编译器通过名称修饰(将函数名转换成内部表示,来描述接口,修饰时使用的约定随编译器而定),来跟踪每一个重载函数。

    函数模板

     函数模板使用泛型来定义函数。

    定义普通模板的示例:

    template <typename T> //or "class T"
    void Swap(T &a, T &b)
    {
      pass;  
    }

    函数模板不能用来缩短可执行函数,最终代码不包含任何模板,而只包含程序生成时的实际函数。

    使用模板的好处是,它使生成多个函数定义更简单、更可靠。

    模板通常放在头文件中。

    并非所有的模板都必须是模板参数类型,模板函数也可以重载。

    模板的局限性:

    有些类型并不支持一些函数内定义的运算,这时就对一些类型造成了限制。

    有两种解决方案:1. 重载运算符; 2. 为具体类型提供具体化的模板定义。

    模板的具体化:

    1. 对于给定的函数名可以有非模板函数、模板函数和显示具体化模板函数以及它们的重载版本;

    2. 显示具体化的原型和定义以template <>开头;

    3. 具体化优先于常规模板,而非模板函数优先于具体化和常规模板。

    隐式实例化(implicit instantiation)、显式实例化(explicit instantiation)和显式具体化(explicit specialization)统称为具体化(specialization)。

    在声明中使用前缀template和template <>以区分显式实例化和显式具体化。

    重载解析:

    C++有一个良好的策略,来决定使用哪一个函数定义。

    使函数调用参数与可行的候选函数的参数匹配所要进行的转换,从最佳到最差的顺序如下所述:

    1. 完全匹配,但常规函数优于模板函数;

    2. 提升转换(例如,char和short自动转换为int,float自动转换为double);

    3. 标准转换(例如,int转换为char,long转换为double)。

    若是指针和引用指向的数据有const和非const之间的区别,不会产生二义性(ambiguous);若是普通常量,则没有const和非const的区别。

    如果两个完全匹配的函数都是模板函数,则较具体的模板函数优先(执行的转换最少的优先)。

    找出最具体的模板的规则被称为函数模板的部分排序规则。

    创建自定义选择

    如lesser(...)有模板函数和非模板函数两个版本,在调用中使用:

    lesser<>(...);
    lesser<int>(...)

    将自定义选择模板函数。

    C++11中模板函数的改善

    1. C++11新增了关键字decltype,解决模板函数中类型定义的问题。

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

    2. C++11后置返回类型。

    auto h(int x, float y) -> double
    {/*function body*/}

    其中auto是一个占位符,表示后置返回类型提供的类型。

    //

    #include<iostream>
    using namespace std;
    struct ss{
    double m;
    };

    
    

    template<typename T>
    void print_f(T &);

    
    

    //
    template<typename T1>
    void print_f(const T1 &);

    
    

    //显式具体化
    template <> void print_f(ss &);
    //

    
    

    void print_f(const int);

    
    


    int main()
    {
    int s0 = 10;
    print_f(s0);//A使用非模板函数

    
    

    int s = 10;
    print_f<>(s);//B显式实例化

    
    

    const double s1 = 10;
    print_f(s1);//C隐式实例化

    
    

    const char sc = 10;
    print_f<int>(sc);//C显式实例化

    
    

    ss s2;
    s2.m = 10;
    print_f(s2);//D显式具体化

    
    

    system("pause");
    return 0;
    }

    
    

    void print_f(const int f)
    {
    cout << "A print " << f << endl;
    cout << "size " << sizeof f << endl;
    cout << "*************************** ";
    }

    
    

    template<typename T>
    void print_f(T &f)
    {
    cout << "B print " << f << endl;
    cout << "size " << sizeof f << endl;
    cout << "*************************** ";
    }

    
    

    template<typename T1>
    void print_f(const T1 &f)
    {
    cout << "C print " << f << endl;
    cout << "size " << sizeof f << endl;
    cout << "*************************** ";
    }

    
    

    //显式具体化
    template <> void print_f(ss &f)
    {
    cout << "D print " << f.m << endl;
    cout << "size " << sizeof f << endl;
    cout << "*************************** ";
    }

     
  • 相关阅读:
    AES算法,DES算法,RSA算法JAVA实现
    spring官方学习地址
    逐步理解SpringMVC
    sublime前端开发工具常用技巧
    谈谈关键字new
    关于mybatisgenerator的问题
    AOPjdk动态代理的思考
    关于java解析xml文件出现的问题
    Java注解
    git向码云上传代码总结
  • 原文地址:https://www.cnblogs.com/sungnox/p/7611795.html
Copyright © 2020-2023  润新知