1 问题引入
在CSDN论坛上有朋友谈到以下问题,在main()函数之前并没有声明swap()函数,但是程序没有报错,并且正确完成了两个整形变量值之间的互换。代码如下所示:
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
int a = 3, b = 4;
swap(a, b);
cout<<"a="<<a<<",b="<<b<<endl;
return 0;
}
void swap(int a1, int b1)
{
int c;
c = a1;
a1 = b1;
b1 = c;
}
2 问题分析
2.1 函数声明
正如变量必须先声明后使用一样,函数也必须在被调用之前先声明。与变量的定义类似,函数的声明也可以和函数的定义分离,当然也可以定义的同时声明;一个函数只能定义一次,但是可以声明多次。函数的声明由函数返回类型、函数名和形参列表组成。形参列表必须包括形参类型,但不必对形参命名。
从以上介绍可以看出,在main()函数中调用swap()之前,必须要对swap()函数进行声明,但是程序中并没有对swap()函数声明并且可以成功调用swap()函数,主要原因就是程序中用到了命名空间。
2.2 命名空间
说起命名空间,就不得不提C++标准库。C++标准库涵盖范围非常大,包含了许多功能。所以标准库与第三方提供的程序库中的类型名称和函数名称可能会发生冲突。为了避免这个问题,C++标准库被放在名为“std”的空间中。由于标准库都包含在std空间中,当使用标准库中的函数或者类型时,必须显示地表达出使用的是命名空间std下的名字。例如,要使用std命名空间中的cin,则必须写成
std::cin
其中,::叫做作用域操作符,表示使用的是定义在std命名空间中的cin。很明显,通过作用域操作符来使用std命名空间中的函数或者类型是很麻烦的。可以使用using声明来进行化简。在程序的起始位置处加入如下代码
using namespace std;
一旦使用using声明,我们就可以直接引用函数或者类型,而不需要再引入命名空间的名字了。即此时可以直接使用cin了。
从以上分析可以,上述程序中使用了C++标准库的命名空间std。因此,就可以使用std中的函数而不需要在函数名之前添加std命名空间。而C++标准库中有一个swap()函数模板,定义如下
template<class _Ty> void std::swap(_Ty& _Left, _Ty& _Right);
所以,在main()函数中调用的swap()函数并不是自定义的swap()函数,而是std::swap()函数模板!
2.3 函数的非引用形参和引用形参
每次调用函数时,都会重新创建该函数所有的形参,此时所传递的实参将会初始化对应的形参。也就是说,对于参数是非引用类型的形参,在函数内部修改的是函数重新创建的实参副本,而不是实参本身。
在本程序中自定义的swap()函数包含了两个非引用类型的参数,在main()函数中调用该函数
swap(a, b);
在swap()函数中虽然对a和b进行了修改,实际上修改的并不是实参a和b,而是它们的副本。也就是说,此时a和b的值并没有进行交换。
对于函数的引用类型的形参来说,它们直接关联到其绑定的对象,而并非这些对象的副本。也就是说,将swap()函数的两个参数定义为引用类型时,swap()函数对形参的修改,就是对实参本身的修改。
所以,如果要使用自定义的swap()函数,则需要将swap()函数的形参改为引用类型。
3 问题解决
3.1 使用std命名空间中的swap()函数
如果要直接使用std命名空间中的swap()函数,则将swap()函数的定义代码删掉即可。
3.2 使用自定义的swap()函数
如果要使用自定义的swap()函数,则首先需要在main()函数之前对swap()函数进行声明,之后将swap()函数的两个参数改为引用类型。
修改后的代码如下所示
#include "stdafx.h"
#include <iostream>
using namespace std;
void swap(int &a1, int &b1);
int _tmain(int argc, _TCHAR* argv[])
{
int a = 3, b = 4;
swap(a, b);
cout<<"a="<<a<<",b="<<b<<endl;
return 0;
}
void swap(int &a1, int &b1)
{
int c;
c = a1;
a1 = b1;
b1 = c;
}