函数
关于函数,这里有意识地区分argument
和parameter
,因为它们中文翻译都是参数,这里就直接用英文表述。
局部对象
在C++中,名字有作用域(scope),对象有生命周期(lifetime)。这两个概念很重要:
- 名字的作用域:是否可见(visible)
- 对象的生命周期:是否存在(exist)
分开编译
假设我们的func
函数定义在fact.cc
文件里,其声明在一个名为chapter5.h
的头文件里面。
为了使用这函数,fact.cc
必须包含这个头文件。最后main
函数在factMain.cc
文件里调用
这个fact
函数。为了生成可执行文件,我们必须告诉编译器在哪去找到我们要用的代码。
CC factMain.cc fact.cc
其中CC
是编译器的名字。
如果我们只改了其中一个文件的源文件,我们希望只重现编译修改的文件。大多数编译器都提供分开编译的方式。
这个过程一般是生成以.obj
(Windows)或.o
(UNIX)为后缀的文件,包含着目标代码。然后编译器通过链接(link)
这些文件组成可执行文件。
CC -c factMain.cc #生成factMain.o
CC -c fact.cc # 生成fact.o
CC factMain.o fact.o # 生成可执行文件
Argument传递
通过值传递(passing by value)
当初始化一个非引用类型的变量,初始器的值被拷贝;改变变量不会对初始化器有影响。
通过值传递的Argument工作方式和这一样。
对于指针Parameter:
指针的行为就是一个非引用类型的。
int n = 0, i = 42;
int *p = &n, *q = &i;
*p = 42;
p = q; // 现在p也指向i,但是i和n的值没变
我们来看一个函数,
void reset(int *ip)
{
*ip = 0; // 改变了ip指向的值
ip = 0; // 仅改变了局部的ip的拷贝;argument没有变
}
int i = 42;
reset(&i); // 改变了i,而不是i的地址
cout << i; // 0
熟悉C的程序员常使用指针parameter来访问函数外的对象;但在C++,推荐使用引用parameter。
通过引用传递(passing by reference)
记住,对引用的操作,就是对对象的操作。
我们重新使用引用实现`reset()`。
```c++
void reset(int &i)
{
i = 0;
}
int j = 42;
reset(j);
这时调用reset
,我们传递的是对象本身,而不是地址。
使用引用来避免拷贝:
拷贝大对象或大的容器是低效的。并且,有些类的对象(比如IO类型)是无法被拷贝的。当把无法拷贝的对象作为parameter时,必须使用引用。
bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
因为字符串可能很长,使用引用可以避免拷贝。同时,在函数内部,是不会去改变这些字符串的,所以加上const
。
另外,我们注意到:使用引用的话,函数相当于可以“返回多个值”。
可变parameter的函数
有时,我们事先并不知道要传递多少个argument。C++ 11的新标准提供两种方法来使函数可以接受 可变数目的argument:
- 如果所有argument的类型相同,可以使用库类型
initializer_list
- 如果argument的类型不同,我们可以将其写成一个可变参数模版(variadic template),这在后面介绍模版时会讲
我们先来看看initializer_list
,它在initializer_list
头文件里面,表示一个数组。
有关initializer_list
的一些操作:
initializer_list |
默认初始化;一个空的list |
---|---|
initializer_list |
使用多个const元素初始化 |
lst2(lst) | 拷贝或赋值但并不会拷贝list的元素,拷贝之后会共享元素 |
lst2 = lst | 拷贝或赋值但并不会拷贝list的元素,拷贝之后会共享元素 |
lst.size() | 个数 | |
lst.begin() | 指向第一个元素的指针 |
lst.end() | 指向最后一个元素之后的指针 |
和vector不同的是,initializer_list的元素都是const的。
给它传递一组值时,必须使用花括号。
void error_msg(initializer_list<string> il)
{
for(auto beg = il.begin(); beg != il.end(); ++beg)
cout << *beg;
}
error_msg({"functionX", "Error"});
上面的程序,我们也可以使用range for
来遍历。