C++ Primer 学习笔记11 函数(函数调用、引用形参、非引用形参)

函数的定义

求两个int型的最大公约数

int gcd(int v1,int v2)
{
    while (v2)
    {
        int temp = v2;
        v2 = v1 % v2;
        v1 = temp;
    }
    return v1;
}

1、函数调用

函数名(实参);

#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int gcd(int v1,int v2);
int main()
{
    cout<<"enter two numbers:\n";
	int i,j;
	cin>>i>>j;
	cout<<gcd(i,j)<<endl;
	system("pause");
	return 0;
}

int gcd(int v1,int v2)
{
	int temp;
	while(v2)
	{
	temp=v2;
	v2=v1%v2;
	v1=temp;
	}
	return v1;
}

函数在定义或者声明时,没有显式指定返回类型是不合法的。

如果两个参数具有相同的类型,则其类型必须重复声明:

int manip(int i,j)         //error
{
	//......
}
int manip(int i,int j)         //ok
{
	//......
}

参数表中不能出现同名的参数。

2、参数传递

每次调用函数时,都会重新创建该函数的所有形参,此时所传递的实参将初始化对应的形参。

形参的初始化与变量的初始化一样:如果形参具有非引用类型,则复制实参的值;如果形参为引用类型,则它只是实参的别名。

2.1、非引用形参

普通的非引用类型的参数通过复制对应的实参实现初始化。当用实参副本初始化形参时,函数并没有访问调用所传递的实参本身,因此不会修改实参的值。

int gcd(int v1,int v2)
{
	int temp;
	while(v2)
	{
	temp=v2;
	v2=v1%v2;
	v1=temp;
	}
	return v1;
}

While循环虽然修改了v1和v2的值,但这些仅局限于局部参数,而对调用gcd函数使用的实参没有任何影响。于是调用

gcd(i,j);则i与j的值不受gcd内执行的赋值操作的影响。

1、指针形参

函数的形参可以是指针,此时将复制实参指针。与其他非引用类型的形参一样,该类形参的任何改变也仅作用于局部副本。如果函数将新指针值赋给形参,主调函数使用的实参指针的值没有改变。

如果函数形参是非const类型的指针,则函数可通过指针实现赋值,修改指针所指向对象的值。

#include <iostream>
using namespace std;

void reset(int *ip)
{
    *ip = 0; // changes the value of the object to which ip points
    ip = 0; // changes only the local value of ip; the argument is unchanged
}
int main()
{
    int i = 42;
    int *p = &i;
    cout << "i: " << *p << '\n'; // prints i: 42
    reset(p);                    // changes *p but not p
    cout << "i: " << *p << endl; // ok: prints i: 0
}

如果需要保护指针所指向对象的值,则形参需定义为const对象的指针:

void use_ptr(const int *p)
{
}

我们既可以用int*也可以用const int*类型的实参调用use_ptr函数;但仅能用int*类型的实参传递给reset函数。这个差别来源于指针的初始化规则。可以将指向const对象的指针初始化为指向非const对象,但不可以让指向非const对象的指针指向const对象。

2、const形参

如果函数使用非引用的非const形参,则即可给该函数传递const实参也可传递非const实参。例如可以传递两个int型const对象调用gcd函数

const int i=3,j=6;
int k=gcd(i,j);

如果形参定义为非引用的const类型:

void fcn(const int i)
{   }

则在函数中,不可以改变实参的局部副本。由于实参仍然是以副本形式传递,因此传递给fcn的既可以是const对象也可以是非const对象。

3、复制实参的局限性

复制实参并不是在所有的情况下都适合,不适宜复制实参的情况包括:

• 当需要在函数中修改实参的值时。

• 当需要以大型对象作为实参传递时。对实际的应用而言,复制对象所付出的时间和存储空间代价往往过大。

• 当没有办法实现对象的复制时。

对于上述几种情况,有效的解决办法是将形参定义为引用或指针类型。

2.2、引用形参

交换两个实参的值:

#include <iostream>
using namespace std;
void swap(int v1, int v2)
{
    int tmp = v2;
    v2 = v1;     // assigns new value to local copy of the argument
    v1 = tmp;
}               // local objects v1 and v2 no longer exist
int main()
{
    int i = 10;
    int j = 20;
    cout << "Before swap():\ti: "
         << i << "\tj: " << j << endl;
    swap(i, j);
    cout << "After swap():\ti: "
         << i << "\tj: " << j << endl;
	system("pause");
    return 0;
}

结果显示如下,两个实参的值并未交换,执行swap函数只是交换了实参的局部副本。


为了交换实参的值,需要将形参定义为引用类型

void swap(int &v1, int &v2)
{
    int tmp = v2;
v2 = v1;     
    v1 = tmp;
}     

引用形参直接关联到其所绑定的对象,而并非这些对象的副本。因此,当调用swap

swap(i, j);

形参v1只是对象i的另一个名字,而v2则是对象j的另一个名字。对v1的任何修改实际上也是对i的修改。 

如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为const引用。

如果函数具有非const引用形参,则不能用const对象进行调用,因为此函数可以修改传递进来的对象,这就违背了实参的const特性。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值