C++Primer-day2 第六章

内联函数和constexpr函数

上一节编写的比较两个string长度的函数有很多好处:
1、阅读和理解shorterString函数的调用要比读懂等价的条件表达式容易得多
2、使用函数可以确保行为的统一,每次相关操作能保证按照同样的方式进行
3、如果需要修改计算过程,显然更加容易
4、函数可以重复利用
缺点:调用函数一般比求等价表达式的值要慢一些。在大多数机器上,一次函数调用其实包含着一系列工作:调用前要先保存寄存器,并在返回时恢复;可能需要拷贝实参;程序转向一个新的位置继续执行

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

通常就是将它在每个调用点上“内联地”展开,在函数的返回类型前加上关键字inline,内联说明只是向编译器发出一个请求,编译器可以选择忽略这个请求

inline const string &
shorterString(const string &s1,const string &s2)
{
    return s1.size()<=s2.size()?s1:s2;
}

内联机制用于优化规模较小、流程直接、频繁调用的函数。很多编译器不支持内联递归函数

constexpr函数

constexpr函数是指能用于常量表达式的函数,遵循约定:
函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句

    constexpr int new_sz(){return 42;}
    constexpr int foo=new_sz();

把new_sz定义成无参数的constexpr函数,因为编译器能在程序编译时验证new_sz函数返回的是常量表达式,所以可以用new_sz函数初始化constexpr类型变量foo
执行该初始化任务时,编译器把对constexpr函数的调用替换成结果值,为了能在编译过程中随时展开,constexpr函数可以有空语句、类型别名及using声明。
我们允许constexpr返回值并非一个常量:

//如果arg时常量表达式,那么scale也是常量表达式
constexpr size_t scale(size_t cnt){return new_sz()*cnt;}

当scale的实参是常量表达式时,它的返回值也是常量表达式

    int arr[scale(2)];
    int i=2;
    int a2[scale(i)];

constexpr函数不一定返回常量表达式
内联函数的定义应该放在头文件中。因为内联函数的定义对编译器而言必须是可见的,以便编译器能够在调用点内联展开该函数的代码,所以仅有函数的原型不够。并且,与一般的函数不同,内联函数有可能在程序中定义不止一次,此时必须保证在所有源文件中定义完全相同,把内联函数的定义放在头文件中可以确保这一点。

调试帮助

    string word;
    if(0<1)
    {
        cerr<<"Error:"<<__FILE__
        <<":in function "<<__func__
        <<" at line "<<__LINE__<<endl
        <<"        Compiled on "<<__DATE__
        <<"at "<<__TIME__<<endl
        <<"      Word read was \""<<word
        <<"\":length too short"<<endl;
    }

关闭调试状态:

#include<iostream>
#include<vector>
#define NDEBUG
using namespace std;

void print(vector<int> vi,unsigned index)
{
    unsigned sz=vi.size();

    #ifndef NDBUG
    cout<<"vector对象的大小是:"<<sz<<endl;
    #endif // NDBUG
    if(!vi.empty()&&index<sz){
        cout<<vi[index]<<endl;
        print(vi,index+1);
    }
}
int main()
{
    vector<int> v={1,3,5,7,9,11,13,15};
    print(v,0);
    return 0;
}

函数指针

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

//比较两个string类型的长度
bool lengthCompare(const string &,const string &);

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

//pf指向一个函数,该函数的参数是两个const string的引用,返回值是bool类型
bool (*pf) (const string &,const string &);

pf前边有个*,因此pf是指针;右侧是形参列表,表示pf指向的是函数;再观察左侧,发现函数的返回类型是布尔值。因此,pf就是一个指向函数的指针,其中该函数的参数是两个引用
如果不加*pf两端的(),则pf是一个返回值为bool指针的函数

使用函数指针

当我们把函数名作为一个值使用时,该函数自动地转换成指针。例如按照如下形式我们可以将lenghCompare的地址赋给pf:

pf=lengthCompare; //pf指向名为lengthCompare的函数
pf=&lengthCompare;//等价的赋值语句

此外还能直接使用指向函数的指针调用该函数,无需提前解引用指针:

bool b1=pf("hello","goodbye");            //调用lengthCompare函数
bool b2=(*pf) ("hello","goodbye");        //等价
bool b3=lengthCompare("hello","goodbye"); //等价

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

string::size_type sumLength(const string&,const string&);
bool cstringCompare(const char*,const char*);
pf=0;            //正确:pf不指向任何函数
pf=sumLength;    //错误:返回类型不匹配
pf=cstringCompare; //错误:形参列表不匹配
pf=lengthCompare; //正确:函数和指针类型匹配
重载函数的指针

使用重载函数必须清晰地界定到底应该选用哪个函数。如果定义了指向重载函数的指针

void ff(int*);
void ff(unsigned int);

void (*pf1) (unsigned int)=ff;//pf指向ff(unsigned);
函数指针形参

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

void useBigger(const string &s1,const string &s2,
               bool pf(const string &,const string &));
//等价
void useBigger(const string &s1,const string &s2,
               bool (*pf) (const string &,const string &));

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

//自动将函数lengthCompare转换成指向该函数的指针
useBigger(s1,s2,lengthCompare);

类型别名和decltype能让我们简化使用了函数指针的代码
decltype的结果是函数类型

//函数类型
typedef bool Func(const string &,const string &);
typedef decltype(lengthCompare) Func2;
//指向函数的指针
typedef bool (*FuncP) (const string&,const string&);
typedef decltype(lengthCompare) *FuncP2;

重新声明useBigger:

void useBigger(const string &s1,const string &s2,Func);
//等价
void useBigger(const string &s1,const string &s2,FuncP2);
返回指向函数的指针

能返回指向函数类型的指针。必须把返回类型写成指针形式,编译器不会自动的将函数返回类型当成对应的指针处理,声明一个返回函数指针的函数

using F=int(int*,int);      //F是函数类型,不是指针
using PF=int (*)(int*,int); //PF是指针类型:指向函数类型的指针

返回类型不会自动地转换成指针。必须显式地将返回类型指定为指针:

PF f1(int);//正确:PF是指向函数的指针,f1返回指向函数的指针
F f1(int);//错误:F是函数类型,f1不能返回一个函数
F *f1(int); //正确:显式地指定返回类型是指向函数的指针

int (*f1(int)) (int*,int);

使用尾置返回类型的方式声明一个返回函数指针的函数

auto f1(int)->int (*)(int*,int);
将auto和decltype用于函数指针类型

如果明确知道返回的函数是哪一个,能使用decltype简化书写函数返回指针返回类型的过程,假定有两个函数返回类型都是string::size_type,并且各有两个const string&类型的形参,此时编写第三个函数,它接受一个string类型的参数,返回一个指针

string::size_type sumLength(const string&,const string&);
string::size_type largerLength(const string&,const string&);
//根据其形参的取值,getFcn函数返回指向其中一个函数的指针
decltype(sumLength) *getFcn(const string&);
练习

6.54:编写函数的声明,令其接受两个int形参并且返回类型也是int;然后声明一个vector对象,令其元素是指向该函数的指针。

int func(int,int);
vector<decltype(func) *> vf;

6.55:
编写4个函数分别进行加减乘除,在vector保存这些指针,输出结果

#include<iostream>
#include<vector>
using namespace std;

int func1(int a, int b) {
    return a + b;
}

int func2(int a, int b) {
    return a - b;
}

int func3(int a, int b) {
    return a * b;
}

int func4(int a, int b) {
    return a / b;
}

void compute(int a,int b,int (*p)(int,int))
{
    cout<<p(a,b)<<endl;
}

int main()
{
    int i=5,j=10;
    decltype(func1) *p1=func1,*p2 = func2, *p3 = func3, *p4 = func4;
    vector<decltype(func1) *>vf={p1,p2,p3,p4};
    for(auto p:vf)
    {
        // 遍历 vector 中的每个元素,依次调用四则运算函数
        compute(i,j,p);
    }
    return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值