c++ primer读书笔记6.1~6.2

本文介绍了C++中的函数基础知识,包括函数的定义、执行流程、返回值等,并详细探讨了参数传递的不同方式,如传值、传引用及数组形参的处理方法。此外,还讨论了可变形参的应用。

6.1 函数基础

我们通过调用运算符来执行函数。调用运算符的形式是一对圆括号。它作用于一个表达式,该表达式是函数或者指向函数的指针。
执行函数的第一步是定义并初始化函数的形参。
return语句用于返回值,并将控制权从被调函数转移回主调函数。
尽管函数调用过程中,实参和形参存在对应关系,但是并没有规定实参的求值顺序,编译器可以以任意可行的顺序对实参求值。要特别注意此点。
函数的形参列表可以为空,但是不能省略。不过为了与c兼容,也可以使用关键字void表示函数没有形参。形参可以没有名字,通常这表示在函数体内不会使用到此形参。但是即使某个形参不被函数使用,也必须为它提供一个实参。
函数的返回值不能是数组或者函数,但是可以是指向函数或者数组的指针(或引用)。

局部对象

形参和函数体内定义的变量统称为局部变量。只存在于块执行期间的对象称为自动对象。对于局部变量对应的自动对象来说,其定义时若没有初始值,则将执行默认初始化,这意味着其将产生未定义的值。
我们可以将局部对象定义为static类型使其称为局部静态对象,这样对象的生命周期将持续到程序终止为止。如果局部静态变量没有默认的初始值,它将执行值初始化,即内置类型的局部静态变量将初始化为零值。

6.2参数传递

形参初始化的机理与变量初始初始化一致。有传引用调用、传值调用两种形式,而传值调用中又可以传递指针。
通常来说我们应该尽量传递引用来避免对象的拷贝,如果函数无需改变引用形参的值,那么我们最好把形参声明为常引用。而且传递引用为我们在一个函数中返回多个值提供了有效的途径。

const形参和实参

我们知道,顶层const作用于对象本身,底层const作用于当前对象所指示的那个对象。和其他初始化一样,用实参初始化形参是会忽略顶层const,即非const的实参可以用来初始化const的形参类型,所以通常我们应该把形参定义为const类型。
另外,在我们不能仅通过形参是否是const类型来区分两个重载函数

void fcn(const int i);
void fcn(int i);        //错误,重复定义了函数fcn,此函数与上面的同名函数并不是重载函数

对于底层const,我们可以使用非const对象初始化一个底层const对象,但是翻过来不行。

int i = 42;
int &r = i;
const int &r2 = 42;

void reset(const int &i);
void fcn(int &i);

reset(r);
reset(r2);
fcn(r);
fcn(r2);        //错误,不能用const int&类型初始化int &类型的形参

在实际编程中药尽量将函数的形参定义为常引用。

数组形参

但我们传递一个数组时,实际上传递的是指向数组首元素的指针。尽管我们不能以值传递的方式传递数组,但是我们可以把形参写成类似数值的形式。

void print(const int[10]);      //这里的数组维度没有任何实际意义,你可以传入一个任意长度的整形数组作为实参
void print(const int *);        //与上述函数声明是同一个性质

和其他使用数组的代码一样,以数组为形参的函数也必须确保使用数组时不会发生越界。因为数组十一指针的形式传递给函数的,所以函数一开始并不知道数组的确切尺寸,所以调用者应该为此提供一些额外的信息。常见的处理方法有如下几种:

使用标记指定数组长度

c风格字符串就是使用这种方法:

void print(const char *cp)
{
    if(cp)
        while(*cp)        //判断cp是不是一个空字符
            cout << *cp++;
}

使用标准库规范

第二种技术是传递指向数组首元素和尾后元素的指针

void print(const int *beg, const int *end)
{
    while(beg != end)
        cout << *beg++ << endl;
}

int j[2] = {0, 1};
print(beg(j), end(j));          //使用c++11的新标准库函数

显式传递一个表示数组大小的形参

void print(const int ia[], size_t size);

数组引用形参

c++允许将变量定义为数组的引用,基于同样的道理,形参可以是数组的引用。

void print(int (&arr)[10])      //要特别注意数组的引用形式
{
    for(auto elem : arr)
        cout << elem << endl;
}

此种方法要注意的是,数组的维度也是数组类型的一部分,所以这个函数只能传入一个维度为10的int数组作为实参,这无形之中限制了函数的可用性。

传递多维数组

和所有数组一样,当将多维数组传递给函数时,真正传递的是指向数组首元素的指针。区别在于多维数组的数组首元素本身就是一个数组,指针就是一个指向数组的指针。此时数组第二维的大小也是数组类型的一部分。

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

要特别注意区分以下两种形式

int *matrix[10];    //表示一个指针数组
int (*matrix)[10];  //表示一个指向数组的指针

当然我们也可以使用数组的语法定义函数,此时编译器将自动忽略第一维的大小,但是第二维的大小将视为形参类型的一部分。

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

含有可变形参的函数

为了编写可以处理不同数量实参的函数,c++ 11提供了两种主要解决方法:如果所有实参类型相同,则可以传递一个名为initializer_list的标准库类型;如果实参的类型不同,我们可以编写一种特殊的函数,也就是所谓的可变参数模板(以后介绍)。
c++还有一种特殊的形参类型,即省略号“…”,它可以用来传递可变数量的实参,其实这种功能时从c继承来的,一般只用于与c函数交互的接口程序。

initalizer_list

  initalizer_list是一种标准库模板。initalizer_list对象中的元素永远是常量值,我们无法改变initalizer_list对象中元素的值。如果想向initalizer_list形参传递一个值得序列,则必须把序列放在一对花括号之中,且含有initalizer_list形参的函数也可以含有其他形参。因为initalizer_list包含begin.end成员,所以我通常使用范围for循环来处理其中的元素。
  

void error_msg(Errcode e, initalizer_list<string> il)
{
    cout << e.msg() << ":";
    for(const auto &elem : il)
        cout << elem << " ";
    cout << endl;
}

error_msg(Errcode(42), {"function", "okay"};

省略符形参

省略福形参不会进行类型检查,所以可以传递任意数量不同类型的实参,但是要注意,省略符形参应该仅仅用于c与c++通用的类型,大多数类类型的对象在传递给省略符号形参时都无法正确拷贝。

void foo(...);  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值