函数

关于函数,这里有意识地区分argumentparameter,因为它们中文翻译都是参数,这里就直接用英文表述。

局部对象

在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 lst 默认初始化;一个空的list
initializer_list lst{a,b,c...} 使用多个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来遍历。