C++ 基础与深度分析 Chapter6 函数(函数重载与重载解析、内联函数、constexpr函数、函数指针)

函数重载与重载解析

在这里插入图片描述

函数重载

函数重载是C++引入的,C语言没有。

int fun(int x)
{
    return x + 1;
}

int main()
{
   cout << fun(3) << endl; // 4
   cout << fun(3.5) << endl; // 4  warning: implicit conversion from 'double' to 'int' changes value from 3.5 to 3 [-Wliteral-conversion]
}

函数重载(参数类型不同)

int fun(int x)
{
    return x + 1;
}

double fun(double x)
{
    return x + 1;
}

int main()
{
   cout << fun(3) << endl; // 4
   cout << fun(3.5) << endl; // 4.5 函数重载 调用double fun(double x)
}

函数重载(参数个数不同)

int fun(int x)
{
    return x + 1;
}


int fun(int x, int y)
{
    return x + y;
}


int main()
{
   cout << fun(3) << endl; // 4
   cout << fun(3, 4) << endl; // 7 函数重载 调用 int fun(int x, int y)
}

不能急于不同函数返回类型进行函数重载
在这里插入图片描述
上述情况是重定义
函数重载与name mangling
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
编译器如何选择正确的函数版本来进行调用:重载解析
在这里插入图片描述

名称查找 name lookup

给一个函数,系统要知道调用哪个函数去。

限定的名称查找,对查找的区域进行了限定,编译器会根据限定去查找

void fun()
{
    cout << "global fun is called" << endl;
}

namespace MyNS
{
    void fun()
    {
        cout << "MyNS fun is called" << endl;
    }
}

int main()
{
	// 限定的名称查找
    ::fun(); // 全局的fun函数调用
    MyNS::fun(); // namespace MyNS中的fun被调用
}

非限定的名称查找

void fun()
{
    cout << "global fun is called" << endl;
}

namespace MyNS
{
    void fun()
    {
        cout << "MyNS fun is called" << endl;
    }
}

int main()
{
    fun(); // global fun is called 非限定名称查找,调用了全局的
}

非限定的名称查找会进行域的逐级查找,造成名称的隐藏。

void fun()
{
    cout << "global fun is called" << endl;
}

namespace MyNS
{
    void fun()
    {
        cout << "MyNS fun is called" << endl;
    }
    
    void g()
    {
        fun(); // 这里会调用哪个fun呢?
    }
}

int main()
{
    MyNS::g(); // "MyNS fun is called" 调用了MyNS中的fun函数。域的逐级查找
    // g属于MyNS,系统会先在MyNS中查找有没有fun函数。
}
void fun()
{
    cout << "global fun is called" << endl;
}

namespace MyNS
{
   
    void g()
    {
        fun(); // 这里会调用哪个fun呢?
    }
}

int main()
{
    MyNS::g(); // "global fun is called" 调用了全局中的fun函数。因为MyNS中没有fun了
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实参依赖查找:ADL(Argument Dependent Lookup)
在这里插入图片描述
注意,ADL只对自定义的类型有效。内建类型无效。

重载解析

在这里插入图片描述
在这里插入图片描述
过滤不能被调用的版本。

#include <iostream>
#include <initializer_list>
#include <string>
using namespace std;

void fun(int x)
{

}

void fun(std::string x)
{

}


int main()
{
    fun(3); // 只会调用第1个fun,第2个被过滤掉
}

过滤不能被调用的版本后,int和double的fun都被保留了。
在剩余的表达式,匹配最匹配的fun函数。
最后3匹配int最好。

#include <iostream>
#include <initializer_list>
#include <string>
using namespace std;

void fun(int x)
{

}

void fun(double x)
{

}


int main()
{
    fun(3);
}

promotion: short到int,int到long
标准转换:int到double。
省略号…:可以匹配任何参数,匹配级别最高,也就是最cheap的匹配。

void fun(int x)
{
    cout << 1;
}

void fun(double x) // 级别3,标准匹配
{
    cout << 2;
}


int main()
{
    fun(3); // 匹配int x,完美匹配
}

在这里插入图片描述
在这里插入图片描述

内联函数 constexpr函数

在这里插入图片描述
递归,就是函数调用自己。
循环是一种特殊的迭代。迭代是循环的扩展。
在这里插入图片描述
在这里插入图片描述

内联函数是一种优化机制。编译器会选择展开还是不展开,我们不用管。
展开的原因是关联的函数比较简单,调用要开栈,销毁等,比较消耗效率,所以就展开了。但是有的函数逻辑比较复杂,编译器不选择展开。

展开不是简单的替换。避免名字的冲突。
在这里插入图片描述
如果程序中,只看到了函数的声明,而没有看到函数的定义,是无法进行展开的。
在这里插入图片描述
在这里插入图片描述
c++标准定义,如果一个函数出现了inline,多个函数中,连接器就选择一个。

constexpr是一个编译器常量,在编译期就可以确定的常量。
在这里插入图片描述

constexpr int fun()
{
    return 3;
}

constexpr int x = fun(); // 因为fun在编译期可以求值,所以在编译期确定x

int main()
{
    
}

在这里插入图片描述
在这里插入图片描述
一般的函数,可以在运行期求值,函数体里可以包含一系列可以work的代码。但是如果有变量需要在运行期求值,那么就不能用constexpr。

consteval函数:函数只能在编译期求值。C++20.
在这里插入图片描述
为什么要引入constexpr,有一些函数写出来,目的就是告诉系统在编译期执行出来。执行效率提升。
在这里插入图片描述
编译器在处理时,是以cpp文件为单元进行处理的。为什么能在编译期处理程序语句呢?所以constexpr必须是在同一个cpp文件内,而不是在别的翻译单元。这有点像内敛函数,进行展开。

函数指针

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
什么是函数的指针类型?
在这里插入图片描述

int inc(int x)
{
    return x + 1;
}

int dec(int x)
{
    return x - 1;
}

using K = int(int);

int main()
{
    K* fun = &inc; // fun是一个指针,指向了inc这个对象
    cout << (*fun)(100) << endl; // 101 *fun解引用
}

我们为什么要引入函数指针这个概念?
作为高阶函数来使用,一个函数可能接收另外一个函数,或者返回另外一个函数。这样的函数是一个高阶函数。

int inc(int x)
{
    return x + 1;
}

int dec(int x)
{
    return x - 1;
}

using K = int(int);

int Twice(K* fun, int x) // 高阶函数,接受一个函数指针
{
    int tmp = (*fun)(x);
    return tmp * 2;
}

int main()
{
    cout << Twice(&inc, 100) << endl; // 202 &inc的形参是一个inc函数指针
    cout << Twice(&dec, 100) << endl; // 传入不同的函数
}
#include <iostream>
#include <initializer_list>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

int myinc(int x)
{
    return x + 1;
}

int mydec(int x)
{
    return x - 1;
}

using K = int(int);

int main()
{
    std::vector<int> a = {1, 2, 3, 4, 5};
    std::transform(a.begin(), a.end(), a.begin(), &myinc); // transform是一个泛型函数
    for (int i = 0; i < 5; ++i)
    {
        cout << a[i] << endl; // 23456
    }

    std::transform(a.begin(), a.end(), a.begin(), &mydec);
    for (int i = 0; i < 5; ++i)
    {
        cout << a[i] << endl; // 01234
    }
}

在这里插入图片描述
函数是不能复制的,就像数组无法复制一样。
在这里插入图片描述
如果把一个对象付给一个函数,这个对象会自动转化成函数指针。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
因为函数不能复制,所以函数返回也是一个函数指针。
函数指针都是与高阶函数配合使用的。


int myinc(int x)
{
    return x + 1;
}

int mydec(int x)
{
    return x - 1;
}

auto fun(bool input)
{
    if (input)
        return myinc;
    else
        return mydec;
}

int main()
{
    cout << (*fun(true))(100) << endl; // 证明返回的是函数指针
}

但在C++有很多函数指针的代替品,比如lambada表达式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值