return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方。 returun语句有两种形式:
return; return expression;
没有返回值的return语句只能用在返回类型是void的函数中。返回void的函数不要求非得有return语句,如果不写,最后会隐式的执行return。
如果void函数想在它中间位置提前退出,可以使用return语句,类似循环中的break语句。如:
//swap函数 void swap(int &v1, int &v2) { if (v1 == v2) { return; //结束被调函数,返回主调函数的调用点 } int tmp = v2; v2 = v1; v1 = temp; //不写return,隐式调用return }
只要函数的返回类型不是void,则该函数内的每条return语句必须返回一个值。并且,return语句返回值的类型必须与函数的返回类型相同,或能隐式转换成函数的返回类型。
①值是如何被返回的
返回一个值的方式和初始化一个变量或形参的方式完全一样:返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。
如:返回两个字符串中较短的一个
string shortString(const string &s1, const string &s2) { return s1.size() < s2.size() ? s1 : s2; } int main() { string s = shortString("abc", "abcd"); //调用点 } //等价于 const string &s1 = "abc"; const string &s2 = "abcd"; string tmp = s1.size() < s2.size() ? s1 : s2; //tmp是个临时量 string s = tmp; //返回的值初始化调用点的临时量tmp,再用tmp初始化s
如果函数返回引用,则该引用仅是它所引用对象的一个别名。
如:返回两个字符串中较短的一个的引用
const string &shortString(const string &s1, const string &s2) { return s1.size() < s2.size() ? s1 : s2; } int main() { string s = shortString("abc", "abcd"); //调用点 } //等价于 const string &s1 = "abc"; const string &s2 = "abcd"; const string &tmp = s1.size() < s2.size() ? s1 : s2; //tmp是个临时量 string s = tmp; //返回的值初始化调用点的临时量tmp,再用tmp初始化s
其中形参和返回类型都是const string的引用,tmp和s1都是string(“abc”)的常量引用,不管是调用函数还是返回结果都不会真正拷贝string对象。
②不要返回局部对象的引用或指针
函数完成后(创建临时量之后),它所占用的存储空间也随之被释放。因此,函数终止意味着局部变量的引用将指向不再有效的内存区域。 返回,局部对象的引用将产生错误。
如:
const string& mainip(string ret) { if (!ret.empty()) { return ret; //错误:返回局部对象的引用 } else { return "Empty"; //错误:"Empty"是局部临时量 } } int main() { string s1 = mainip("abc"); cout << s1 << endl; } //等价于 string ret = "abc"; const string &tmp = ret; //ret在创建完临时量tmp后销毁 string s1 = tmp; //此时,tmp所引用的对象已经销毁,tmp相当于野指针,不能操作
当函数结束后局部变量所占用的空间就随之释放了,所以两条return语句都指向了不再可用的内存空间。
③引用返回左值
函数的返回类型决定函数调用返回的是左值还是右值。调用一个返回引用的函数得到左值,其他返回类型得到右值。我们能为返回类型为非常量引用的函数结果赋值。
如:下标运算符的函数形式get_val
char &getVal(string &str, int i) { return str[i]; } int main() { string s("abc"); getVal(s, 0) = 'A'; cout << s << endl; } //输出结果为Abc //等价于 string s("abc"); string &str =s; int i = 0; char &tmp = str[0]; tmp = 'A'; //临时量tmp是s[0]的一个引用,改变tmp会改变str[0]的值。
④列表初始化返回值
函数可以返回花括号包围的值的列表。类似于其他返回结果,此处的**列表用来初始化函数返回的临时量。**如果列表为空,临时量执行值初始化;否则,返回的值由函数返回类型决定。
如:定义一个getString返回vector<string>{"wo", "ele"}.
vector<string> getString() { return { "wo", "ele" }; } int main() { vector<string> s = getString(); cout << *s.begin() << " " << *(s.begin()+1) << endl; return 0; } //等价于 vector<string> tmp = vector<string>{"wo", "ele"}; vector<string> s = tmp;
因为数组不能被拷贝,所以函数不能返回数组。但是,函数可以返回数组的指针或引用。
直接定义一个返回数组的指针或引用的函数比较麻烦,可以通过两种方法来进行:
如:定义一个函数,将输入数组的第一个数改为0,返回该数组的引用
int (&func(int(&a)[10]))[10] { //可以看出直接表示法太过复杂 a[0] = 0; return a; } void printArr(const int(&a)[10]) { for (int i = 0; i < 10; ++i) { cout << a[i] << " " ; } } int main() { int a[10]{2,2,2,2,2,2,2,2}; func(a)[1] = 3; //由于返回的是数组的引用,因此可以作为左值 printArr(a); return 0; } //输出结果:0 3 2 2 2 2 2 2 0 0
//类型别名定义 using arrT = int[10]; //等价于 //typedef int arrT[10]; arrT& func1(int(&a)[10]) { //可以看出函数的形式清晰了喜多 a[1] = 1; return a; } int main() { int a[10]{2,2,2,2,2,2,2,2}; func1(a)[0] = 3; //返回的是输入数组的引用,因此可以作为左值 printArr(a); return 0; } //输出结果为:3 1 2 2 2 2 2 2 0 0
尾置返回类型对任何函数都能用,但是对于返回类型很复杂的函数最有效,如返回类型是数组的指针或数组的引用。
auto func2(int(&a)[10])->int(&)[10]{ //可以清楚看出返回类型是数组的引用 a[2] = 4; return a; } int main() { int a[10]{2,2,2,2,2,2,2,2}; func2(a)[0] = 3; printArr(a); return 0; } //输出结果为3 2 4 2 2 2 2 2 0 0