栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

文章目录
  • 函数重载与重载解析
    • 函数重载
    • 名称查找 name lookup
    • 重载解析
  • 内联函数 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 
#include 
#include 
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 
#include 
#include 
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 
#include 
#include 
#include 
#include 
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 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表达式。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/980368.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号