zoukankan      html  css  js  c++  java
  • 函数----函数重载,特殊用途语言特性,函数匹配,函数指针

    一、函数重载

      如果同一作用域内的几个函数名字相同但形参列表不同,我们称之为重载函数。

    1、定义重载函数

      对于重载的函数来说,它们应该在形参数量或形参类型上有所不同。

    2、重载和const形参

      一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来:

    1 int func(int x);
    2 int func(const int x); // 重复声明了func(int x)
    3 
    4 int func2(int *);
    5 int func2(int *const); // 重复声明了func2(int *)

      如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,此时的const是底层的:

    1 int func(int &);
    2 int func(const int &); // 新函数,作用于常量引用
    3 
    4 int func2(int *);
    5 int func2(const int *); // 新函数,作用于指向常量的指针

     3、const_cast和重载

      const_cast在重载函数的情景中最有用。

     1 #include <iostream>
     2 #include <string>
     3 
     4 const std::string &shortString(const std::string &s1, const std::string &s2)
     5 {
     6     std::cout << "const" << std::endl;
     7     return s1.size() < s2.size() ? s1 : s2;
     8 }
     9 std::string shortString(std::string &s1, std::string s2)
    10 {
    11     std::cout << "not const" << std::endl;
    12     auto &r = shortString(const_cast<const std::string &>(s1), const_cast<const std::string &>(s2));
    13     return const_cast<std::string &>(r);
    14 }
    15 int main()
    16 {
    17     const std::string s1 = "abc", s2 = "ABC";
    18     std::cout << shortString(s1, s2) << std::endl;
    19     std::cout << "-----------------------" << std::endl;
    20     std::string s3 = "abc", s4 = "ABC";
    21     std::cout << shortString(s3, s4) << std::endl;
    22     return 0;
    23 }

    4、调用重载的函数

       定义了一组重载函数后,我们需要以合理的实参调用它们。函数匹配是指一个过程,在这个过程中我们把函数调用与一组重载函数中的某一个关联起来,函数匹配也叫重载确定。编译器首先将调用的实参与重载集合中每一个函数的形参进行比较,然后根据比较的结果决定到底调用哪个函数。

      当个调用重载函数时有三种可能的结果:

      a、编译器找到一个与实参最佳匹配的函数,并生成调用该函数的代码。

      b、找不到任何一个函数与调用的实参匹配,此时编译器发出无匹配的错误信息。

      c、有多于一个函数可以匹配,但是每一个都不是明显的最佳选择。此时也将发生错误信息,称为二义性调用。

    5、重载与作用域

      重载对作用域的一般性质并没有什么改变:如果我们在内层作用域中声明了一个函数名字,它将隐藏外层作用域中声明的同名实体。

    二、特殊用途语言特性

    1、默认实参

      我们可以为一个或多个形参定义默认值,不过需要注意的是,一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。

      在给定的作用域中一个形参只能被赋予一次默认实参。换句话说,函数的后续声明只能为之前那些没有默认值的形参添加默认值,而且该形参右侧的所有形参必须都有默认值。

      局部变量不能作为默认实参。除此之外,只要表达式的类型能转换成形参所需的类型,该表达式就能作为默认实参。用作默认实参的名字在函数声明所在的作用域解析,而这些名字的求值过程发生在函数调用时

    2、内联函数和constexpr函数

      在大多数机器上,一次函数调用其实包含着一系列工作:调用前要先保存寄存器,并在返回时恢复;可能需要拷贝实参;程序转向一个新的位置继续执行。

    1)内联函数可避免函数调用的开销

       声明内联函数只要在函数的返回类型前面加上关键字inline就行了。

    2)constexpr函数

      constexpr函数是指能用于常量表达式的函数。定义constexpr函数的方法与其他函数类似,不过要遵循几项规定:函数的返回类型及所有形参的类型都是字面值类型,而且函数体中必须有且只有一条return语句。

     1 #include <iostream>
     2 #include <string>
     3 
     4 constexpr int func() {
     5     return 1024;
     6 }
     7 int main()
     8 {
     9     constexpr int x = func();
    10     return 0;
    11 }
    View Code

      执行初始化任务时,编译器把对constexpr函数的调用替换成其结果值。为了能在编译过程中随时展开,constexpr函数被隐式地指定为内联函数。

      constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。

      constexpr函数不一定返回常量表达式。当把constexpr函数用在需要常量表达式的上下文中时,由编译器负责检查函数的结果是否符合要求。如果结果恰好不是常量表达式。编译器将发出错误信息

    3、调试帮助

    1、assert预处理宏

      assert宏定义在cassert头文件中。assert是一种预处理宏。所谓预处理宏其实是一个预处理变量,它的行为有点类似于内联函数。assert宏使用一个表达式作为它的条件:

      assert(expr);

    首先对expr求值,如果表达式为假(即为0),assert输出信息并终止程序的执行。如果表达式为真(即非0),assert什么也不做。

    2、NDEBUG预处理变量

      assert的行为依赖于一个名为NDEBUG的预处理变量的状态。如果定义了NDEBUG,则assert什么也不做。默认状态下没有定义NDEBUG,此时assert将执行运行时检查。

      预处理器定义了几个对调试很有用的名字,如下表所示:

    名字 说明
    __FUNCTION__ 存放函数名的字符串字面值
    __FILE__ 存放文件名的字符串字面值
    __LINE__ 存放当前行号的整型字面值
    __TIME__ 存放文件编译时间的字符串字面值
    __DATE__ 存放文件编译日期的字符串字面值

    三、函数匹配

    1、实参类型转换

       为了确定最佳匹配,编译器将实参类型到形参类型的转换划分成几个等级,具体排序如下所示:

      1)精确匹配,包括以下情况:

      a、实参类型和形参类型相同。

      b、实参从数组类型或函数类型转换成对应的指针类型。

      c、向实参添加顶层const或者从实参中删除顶层const。

      2)通过const转换实现的匹配。

      3)通过类型提升实现的匹配。

      4)通过算术类型转换(所有算术类型转换的级别都一样)或指针转换实现的匹配。

      5)通过类类型转换实现的匹配。

    四、函数指针

       函数指针指向的是函数而非对象。和其他指针一样,函数指针指向某种特定类型。函数的类型由它的返回类型和形参类型共同决定,与函数名无关。

    1 bool lengthCompare(const std::string &s1, const std::string &s2)
    2 {
    3     return s1.size() > s2.size();
    4 }

    该函数的类型是bool(const std::string &, const std::string &)。要想声明一个可以指向该函数的指针,只需要用指针替换函数名即可。

    1     //pf指向一个函数,该函数的参数是两个const string的引用,返回值是bool类型
    2     bool (*pf)(const std::string &, const std::string &); // 未初始化,*pf两端的括号必不可少

    1、使用函数指针

    1     // 当我们把函数名作为一个值使用时,该函数自动地转换成指针
    2     pf = lengthCompare;
    3     pf = &lengthCompare; // 等价的赋值语句:取地址符是可选的
    4     // 此外,我们还能直接使用指向函数的指针调用该函数,无须提前解引用指针
    5     bool b1 = pf("hello", "world");
    6     bool b2 = (*pf)("hello", "world");
    7     bool b3 = lengthCompare("hello", "world"); // 三条调用语句等价

      在指向不同函数类型的指针间不存在转换规则。但是,我们可以为函数指针赋一个nullptr或者值为0的整型表达式,表示该指针没有指向任何一个函数。

    2、函数指针形参

      和数组类似,虽然不能定义函数类型的形参,但是形参可以是指向函数的指针。此时,形参看起来是函数类型,实际上却是当成指针使用。

    1 // 参数是函数类型,它会自动地转换成指向函数的指针
    2 void use(bool pf(const std::string &, const std::string &));
    3 // 等价的声明:显示地将形参定义成指向函数的指针
    4 void use(bool (*pf)(const std::string &, const std::string &));

      可以直接把函数作为实参使用,此时它会自动转换成指针

      use(lengthCompare);

      类型别名可以简化使用了函数指针的代码:

    1     // Func和Func2是函数类型
    2     typedef bool Func(const std::string &, const std::string &);
    3     typedef decltype(lengthCompare) Func2; // 等价的类型
    4     // FuncP和FuncP2是指向函数的指针
    5     typedef bool(*FuncP)(const std::string &, const std::string &);
    6     typedef decltype(lengthCompare) *FuncP2; // 等价的类型

    3、返回指向函数的指针

      和数组类似,虽然不能返回一个函数,但是能返回指向函数类型的指针。然而,必须显示地将返回类型写成指针形式,编译器不会自动地将函数返回类型当成对应的指针类型处理

    4、将auto和decltype用于函数指针类型

      当将decltype作用于某个函数时,它返回函数类型而非指针类型。因此,我们必须显示地加上*以表明我们需要返回指针。

  • 相关阅读:
    【Azure 应用服务】由 Azure Functions runtime is unreachable 的错误消息推导出 ASYNC(异步)和 SYNC(同步)混用而引起ThreadPool耗尽问题
    【Azure API 管理】是否可以将Swagger 的API定义导入导Azure API Management中
    【Azure 应用服务】Azure Function 不能被触发
    【Azure 环境】Azure Key Vault (密钥保管库)中所保管的Keys, Secrets,Certificates是否可以实现数据粒度的权限控制呢?
    【Azure 事件中心】为应用程序网关(Application Gateway with WAF) 配置诊断日志,发送到事件中心
    【Azure 事件中心】azure-spring-cloud-stream-binder-eventhubs客户端组件问题, 实践消息非顺序可达
    【Azure API 管理】Azure API Management通过请求中的Path来限定其被访问的频率(如1秒一次)
    【Azure 环境】前端Web通过Azure AD获取Token时发生跨域问题(CORS Error)
    【Azure 应用服务】记一次Azure Spring Cloud 的部署错误 (az spring-cloud app deploy -g dev -s testdemo -n demo -p ./hellospring-0.0.1-SNAPSHOT.jar --->>> Failed to wait for deployment instances to be ready)
    【Azure 应用服务】App Service中抓取 Web Job 的 DUMP 办法
  • 原文地址:https://www.cnblogs.com/ACGame/p/10181078.html
Copyright © 2011-2022 走看看