c++基础系列:函数

函数基础

函数是一个命名了的代码块,我们通过调用函数执行相应的代码;函数可以有0个多个参数,而且会产生一个结果;可以重载函数,即同一个名字可以对应几个不同的函数。

 一个典型的函数定义包括以下部分:返回类型函数名字、由0个或多个形参组成的列表以及函数体;其中形参以逗号隔开。

通过调用运算符来执行函数;调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是函数或者指向函数的指针;圆括号之内是一个用逗号隔开的实参列表,我们用实参初始化函数的形参,调用表达式的类型就是函数的返回类型。

函数的调用完成两项工作:一是用实参初始化函数对应的形参,二是将控制权转移给被调函数。

实参是形参的初始值,实参的类型必须与对应的形参类型匹配。

函数的形参列表可以为空,但是不能省略。

局部对象

c++中名字的作用域是程序文本的一部分,名字在其中可见;对象的生命周期是程序执行过程中该对象存在的一段时间。

形参和函数体内定义的变量统称为局部变量

可以将局部变量定义成static类型从而获得生命周期贯穿函数调用及之后的时间,即局部静态对象;在程序执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。

size_t count_calls()
{
    static size_t ctr = 0;//调用结束时这个值仍然有效
    return ++ctr;
}

int main()
{
    for(siez_t i = 0; i != 10; ++i)
        cout << count_calls() << endl;
    return 0;
}
//输出1~10

 函数的声明与定义

函数的声明和函数的定义非常类似,唯一的区别是函数声明无须函数体,用一个分号替代即可

因为函数的声明不包含函数体,所以也无须形参的名字,但写上名字可以更好的帮助理解函数。

函数的声明也称为函数原型

函数应该是头文件中声明,在源文件中定义。

参数传递

每次调用函数时都会重新创建它的形参,并用传入的实参对形参进行初始化;该机理与变量初始化一样。

形参的类型决定了形参和实参的交互的方式;如果形参是引用类型,它将绑定到对应的实参上;否则将实参的值拷贝后赋给形参。


引用传递:当形参是引用类型时,则它对应的实参被引用传递或者函数被传引用调用;和其它引用一样,引用形参也是它绑定的对象的别名,即是对应的实参的别名。

值传递:当实参的值被拷贝给形参时,形参和实参是两个相互独立的对象,这样的实参被值传递或者函数被传值调用。

传值参数

当初始化一个非引用类型的变量时,初始值被拷贝给变量;此时对变量的改动不会影响初始值。

int n = 0;//int类型的初始变量
int i = n;//i是n的值的副本
i = 42;//i的值改变,n的值不变

 传值参数的机理完全一样,函数对形参做的所有操作都不会影响实参。

指针形参

指针的行为和其它非引用类型一样,当执行指针拷贝操作时,拷贝的是指针的值;拷贝之后,两个指针是不同的指针;因为指针使我们可以间接地访问它所指向的对象,所以通过指针可以修改它所指对象的值。

int n = 0, i = 42;
int *p = &n, *q = &i;//p指向n,q指向i
*p = 42;//n的值改变,p不变
p = q;//p现在指向了i,但是i和n的值都不变

指针形参的行为与执行指针拷贝操作类似。

建议:c语言常常使用指针类型的形参访问函数外部的对象,在c++中建议使用引用类型的形参代替指针。

传引用参数

对于引用的操作实际上是作用在引用所引 的对象上。

int n = 0, i = 42;

int &r = n;//r绑定了n(即r是n的另一个名字)

r = 42;//现在n的值是42
r = i;//现在n的值和i相同
i = r;//i的值和n相同

使用引用避免拷贝

 拷贝大的类类型对象或者容器对象比较低效,甚至有的类类型(包括IO类型在内)根本不支持拷贝操作;当某种类型不支持拷贝操作时,函数只能通过引用形参该类型的对象。

//比较连个string对象的长度
bool isShorter(const string &s1, const string &s2){
    return s1.size() < s2.size();
}

使用引用形参返回额外信息

一个函数只能返回一个值,但有时候需要同时返回多个值,引用形参为我们一次返回多个结果提供了有效途径

//返回s中c第一次出现的位置索引
//引用形参occurs负责统计c出现的总次数
string::size_type find_char(const string &s, char c, string::size_type &occurs){
    
    auto ret = s.size(); //第一次出现的位置(如果有的话)
    occurs = 0; //设置表示出现次数的形参的值
    for(decltype(ret) i = 0; i != s.size(); ++i){
        if(s[i] == c){
            if(ret == s.size()){
                ret = i;//记录c第一次出现的位置
            }
            ++occurs; //将出现的次数加1
        }
    }
    return ret; //出现次数通过occurs隐式的返回
}

此外要主要const类型修饰的形参和实参以及数组形参。

返回类型和return语句

return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方;有两种形式:

return;

return expression


 没有返回值的return语句只能用在返回类型是void的函数中。

void swap(int &v1, int &v2)
{
    if(v1 == v2)
        return;

    int tmp = v2;
    v2 = v1;
    v1 = tmp;
}

只要函数的返回类型不是void,则该函数内的每条return语句必须返回一个值,且返回类型必须与函数的的返回类型相同,或者能隐式的转换成函数的返回类型。

注意:不要返回局部对象的引用或指针,因为函数完成之后,意味着它所占用的存储空间也随着被释放掉;因此,函数终止意味着局部变量的引用将指向不再有效的内存区域。 

函数重载

如果同一作用域内的几个函数名字相同但形参列表不同,则称之为重载(overload)函数。

注意:main()函数不允许重载。

特殊用途特性

默认实参

某些函数有这样一种形参,在函数的很多次调用中它们都被赋予一个相同的值,此时,把这个反复出现的值称为函数的默认实参。

内联函数

调用函数一般比求等价表达式的值要慢一点,因此将函数指定为内联(inline),通常就是将它在每个调用点上“内联地”展开。

constexpr函数

constexpr函数是指能用于常量表达式的函数,定义与其他函数类型,不过要遵守几项约定:函数的返回类型及所有形参的类型都的是字面值类型,而且函数体内必须有且只有一条return语句。

当然,constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。

constexpr int new_sz(){return 42;}

函数指针

函数指针指向的是函数而非对象,形式:

type (*fun)(type, type)

*fun两端的括号必不可少,如果不写这对括号,则fun是一个返回为type指针的函数。

函数指针和指针函数区别

函数指针只是一个指针,函数指针的作用和指针相似,只是这种指针赋值的函数的地址,函数指针的作用有点像给函数换名字。

int (*fun)(int x,int y);
int add(int x,int y){
    return x+y;
}
int sub(int x,int y){
    return x-y;
}
//函数指针
int (*fun)(int x,int y);

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //第一种写法
    fun = add;
    qDebug() << "(*fun)(1,2) = " << (*fun)(1,2) ;

    //第二种写法,建议用第二种,区别普通函数
    fun = ⊂
    qDebug() << "(*fun)(5,3) = " << (*fun)(5,3)  << fun(5,3);

    return a.exec();
}

指针函数本质是函数,只是返回值是指针,原因是返回值的数据类型先和星号*结合,作为一种指针数据类型创建的函数,这类函数的特点就是,返回值只能是地址。

int *fun(int x,int y);

常见写法:

int *fun(int x,int y);
int * fun(int x,int y);
int* fun(int x,int y);
typedef struct _Data{
    int a;
    int b;
}Data;

//指针函数
Data* f(int a,int b){
    Data * data = new Data;
    data->a = a;
    data->b = b;
    return data;
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //调用指针函数
    Data * myData = f(4,5);
    qDebug() << "f(4,5) = " << myData->a << myData->b;

    return a.exec();
}

你可能感兴趣的:(C++,c++,函数,函数指针,引用,指针,函数重载)