C++Primer学习——第6章 函数

函数

我们用调用运算符(())来执行函数。它作用于一个表达式,该表达式是函数或者执行函数的指针。

局部静态对象

可以将局部变量定义成static类型从而获得这样的对象,。**局部静态对象(local static object)**在程序的执行路径第一次经过对象定义语句初始化,并且直到程序终止才会被销毁,在此期间即使对象所在的函数结束执行也不会对它造成影响。

我们建议变量在头文件中声明,在源文件中定义;函数在头文件中声明而在源文件中定义。

当用实参初始化形参时会忽略掉顶层const。

面对类类型对象或者容器对象时,尽量使用引用,避免拷贝。如函数无须修改引用形参的值,就将其定义为const

在C++中,允许我们定义若干具有相同名字的函数,不过前提是不同函数的形参列表是有明显区别的。

C++允许用字面值常量初始化常量引用。

传递数组引用参数

C++语言允许将变量定义成数组的引用。


void print(int (&arr)[10])
{
    for (auto elem : arr)
        std::cout << elem << std::endl;
}

传递多维数组


void print(int (*matrix)[10], int rowSize);

用引用去传递多维数组

void print(int (&a)[2][10])
{
    for(int i = 0; i < 2; i++)
    {
        for(int j = 0; j < 10; ++j)
            std::cout << a[i][j] << " ";
    }
}

含有可变参数的函数

为了编写能处理不同数量实参的函数,C++11新标准提供了两种主要的方法:

  1. 如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型。
  2. 如果实参的类型不同,可以编写一种特殊的函数,也就是所谓的可变参数模板。

C++还有一种特殊的形参类型(即省略符),可以用它传递可变数量的实参。这种功能一般用于与C函数交互的接口程序。

initializer_list

如果函数的实参数量未知但是全部实参的类型都相同,我们可以它。

initializer_list提供的操作
initializer_list list默认初始化,T类型元素的空列表
initializer_list lst{a,b,c,…};lst的元素数量和初始值一样多;lst的元素是对应初始值的副本;列表中的元素是const的。
lst2(lst) 或 lst2 = lst拷贝或赋值一个initializer_list对象,不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素
lst.size()列表中的元素数量
lst.begin()返回lst中首元素的指针
lst.end()返回lst中尾元素下一个位置的指针

initializer_list对象中的元素永远是常量值,无法改变对象中元素的值。

如果想向initializer_list形参中传递一个值的序列,则必须把序列放在一对花括号内。

省略符形参

省略符形参只能出现在形参列表的最后一个位置:


void foo(parm_list, ...);

void foo(...);

不要返回局部对象的引用或指针

函数完成后,它所占用的存储空间也随之被释放掉。函数终止意味着局部变量的引用将指向不再有效的内存区域。

调用一个返回引用的函数得到左值,其他返回类型得到右值。

列表初始化返回值

C++11新标准规定,函数可以返回花括号包围的值的列表。此次的列表也用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化;否则,返回的值由函数的返回类型决定。

vector<string> process()
{
    //expected 和 actual 是string对象
    if (expected.empty())
        return {}; //返回一个空的vector对象
        
    else if(expected == actual)
            return {"functionX", "okay"}; //返回列表初始化的vector对象
}

如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,而且该值所占空间不应该大于目标类型的空间。如果函数返回的是类类型,由类本身定义初始值如何使用。

尾置返回类型

C++11新标准有一种尾置返回类型(trailing return type)的方法

任何函数的定义都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效。尾置返回类型跟在形参列表后面并以一个->符号开头。为了表示函数真正的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放置一个auto:


auto func(int i) -> int (*)[10];

使用decltype

如果我们知道函数返回的指针将指向哪个数组,就可以使用decltype关键字声明返回类型。


int odd[] = {1, 3, 5, 7, 9};
int even[] = {0, 2, 4, 6, 8};

decltype(odd) *arrPtr(int i)
{
    return (i % 2) ? &odd : &even; //返回一个指向数组的指针
}

重载函数

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

不允许两个函数除了返回类型外其他所有的要素都相同。

重载和cosnt形参

顶层const不影响传入函数的对象。一个拥有顶层const的形参无法和另一个没有顶层const形参区分开来。

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

如果我们在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体。在不同的作用域中无法重载函数名。

默认实参

一旦一个形参被赋予了默认值,则在它后面的所有形参都必须有默认值。

通常应该在函数声明中指定默认实参,并将该声明放在合适的头文件中。

局部变量不能作为默认实参,只要表达式的类型能转换成形参所需的类型,该表达式就能作为默认实参。

用默认实参的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数调用时。

内联函数

内联函数可避免函数调用的开销。将函数指定为内联函数(inline),通常就是将它在每个调用点上“内联地”展开。

在函数的返回类型前面加上关键字inline,这样就可以将它声明成内联函数了。

constexpr函数

**constexpr函数(constexpr function)**是指能用于常量表达式的函数。

函数的返回类型以及所有形参的类型都是字面值类型,而且函数体必须有且只有一条return语句。

constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。允许constexpr函数的返回值并非一个常量。

constexpr函数不一定返回常量表达式。

constexpr函数被隐式地指定为内联函数。

内联函数和constexpr函数通常定义在头文件中。

调试帮助

assert 预处理宏


assert(expr);

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

assert宏常用于检查“不能发生”的条件。

NDEBUG 预处理变量

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

预处理器对于程序调用的名字:

_ _ func_ _ 输出当前调试的函数的名字
_ _ FILE _ _ 存放当前行号的整型字面值
_ _ LINE _ _ 存放当前行号的整型字面值
_ _ TIME _ _ 存放文件编译时间的字符串字面值
_ _ DATE _ _ 存放文件编译日期的字符串字面值

函数指针


bool (*pf) (const string&, const string&);

当我们把一个函数名作为一个值使用时,该函数自动地转换成指针。

我们可以直接使用指向函数的指针调用该函数,无须提前解引用指针。

decltype返回函数类型时,不会将函数类型自动转换成指针类型。

返回一个指向函数的指针


using PF = int (*)(int, int);

声明一个返回函数指针的函数:


auto fi(int) -> int (*)(int, int); //尾式返回类型方式

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值