函数不能返回另一个函数或者内置数组类型,但可以返回指向函数的指针,或指向数组元素的指针的指针:
// ok: pointer to first element of the array
int *foo_bar() { /* ... */ }
形参的初始化与变量的初始化一样:如果形参具有非引用类型,则复制实参的值,如果形参为引用类型(第 2.5 节),则它只是实参的别名。
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 }
void use_ptr(const int *p) { // use_ptr may read but not write to *p }
指针形参是指向 const 类型还是非 const 类型,将影响函数调用所使用的实参。我们既可以用int* 也可以用 const int* 类型的实参调用 use_ptr 函数;但仅能将 int* 类型的实参传递给reset 函数。这个差别来源于指针的初始化规则(第 4.2.5 节)。可以将指向const 对象的指针初始化为指向非 const 对象,但不可以让指向非 const 对象的指针向 const 对象。
// function takes a non-const reference parameter int incr(int &val) { return ++val; } int main() { short v1 = 0; const int v2 = 42; int v3 = incr(v1); // error: v1 is not an int v3 = incr(v2); // error: v2 is const v3 = incr(0); // error: literals are not lvalues v3 = incr(v1 + v2); // error: addition doesn't yield an lvalue int v4 = incr(v3); // ok: v3 is a non const object type int }
问题的关键是非 const 引用形参(第 2.5 节)只能与完全同类型的非const 对象关联。
string &s="Hello World";//errorint &r=12;//error
const string &s="Hello World";ok
const int &r=12;ok
string::size_type find_char(string &s, char c)
{
string::size_type i = 0;
while (i != s.size() && s[i] != c)
++i; // not found, look at next character
return i;
}
这个函数将其 string 类型的实参当作普通(非 const)的引用,尽管函数并没有修改这个形参的值。这样的定义带来的问题是不能通过字符串字面值来调用这个函数:
if (find_char("Hello World", 'o')) // ...
bool is_sentence (const string &s) { // if there's a period and it's the last character in s // then s is a sentence return (find_char(s, '.') == s.size() - 1); }
如上代码,函数 is_sentence 中 find_char 的调用是一个编译错误。传递进is_sentence 的形参是指向 const string 对象的引用,不能将这种类型的参数传递给 find_char,因为后者期待得到一个指向非const string 对象的引用。
应该将不需要修改的引用形参定义为 const 引用。普通的非 const 引用形参在使用时不太灵活。这样的形参既不能用const 对象初始化,也不能用字面值或产生右值的表达式实参初始化。
数组有两个特殊的性质,影响我们定义和使用作用在数组上的函数:一是不能复制数组(第 4.1.1 节);二是使用数组名字时,数组名会自动转化为指向其第一个元素的指针(第 4.2.4 节)。因为数组不能复制,所以无法编写使用数组类型形参的函数。因为数组会被自动转化为指针,所以处理数组的函数通常通过操纵指向数组指向数组中的元素的指针来处理数组。
// three equivalent definitions of printValues
void printValues(int*) { /* ... */ }
void printValues(int[]) { /* ... */ }
void printValues(int[10]) { /* ... */ }
虽然不能直接传递数组,但是函数的形参可以写成数组的形式。
不需要修改数组形参的元素时,函数应该将形参定义为指向 const 对象的指针: |
// f won't change the elements in the array void f(const int*) { /* ... */ }
千万不能返回局部变量的引用。
// Disaster: Function returns a reference to a local object const string &manip(const string& s) { string ret = s; // transform ret in some way return ret; // Wrong: Returning reference to a local object! }
当函数执行完毕,字符串 ret 占用的储存空间被释放,函数返回值指向了对于这个程序来说不再有效的内存空间。
// return plural version of word if ctr isn't 1 string make_plural(size_t ctr, const string &word, const string &ending) { return (ctr == 1) ? word : word + ending; }
这个函数要么返回其形参 word 的副本,要么返回一个未命名的临时 string 对象,这个临时对象是由字符串word 和 ending 的相加而产生的。这两种情况下,return 都在调用该函数的地方复制了返回的 string 对象。
当函数返回引用类型时,没有复制返回值。相反,返回的是对象本身。例如,考虑下面的函数,此函数返回两个 string 类型形参中较短的那个字符串的引用:
// find longer of two strings const string &shorterString(const string &s1, const string &s2) { return s1.size() < s2.size() ? s1 : s2; }
形参和返回类型都是指向 const string 对象的引用,调用函数和返回结果时,都没有复制这些 string 对象。
和返回局部对象的引用一样,返回指向局部对象的指针也是错误的。一旦函数结束,局部对象被释放,返回的指针就变成了指向不再存在的对象的悬垂指针
默认实参是通过给形参表中的形参提供明确的初始值来指定的。程序员可为一个或多个形参定义默认值。但是,如果有一个形参具有默认实参,那么,它后面所有的形参都必须有默认实参。
string screenInit(string::size_type height = 24, string::size_type width = 80, char background = ' ' );
string screen; screen = screenInit(); // equivalent to screenInit (24,80,' ') screen = screenInit(66); // equivalent to screenInit (66,80,' ') screen = screenInit(66, 256); // screenInit(66,256,' ') screen = screenInit(66, 256, '#');
函数调用的实参按位置解析,默认实参只能用来替换函数调用缺少的尾部实参。例如,如果要给 background 提供实参,那么也必须给 height 和 width 提供实参:
screen = screenInit(, , '?'); // error, can omit only trailing arguments screen = screenInit( '?'); // calls screenInit('?',80,' ')
内联函数应该在头文件中定义,这一点不同于其他函数。 |
inline 函数的定义对编译器而言必须是可见的,以便编译器能够在调用点内联展开该函数的代码。此时,仅有函数原型是不够的。
编译器隐式地将在类内定义的成员函数当作内联函数