1. 背景
函数返回值就是使用return
语句终止正在执行的函数,看是很简单的问题有什么说的呢?因为越是简单的问题里面越是有一些不易发现的坑。
比如在循环中使用return语句:
bool findChar(const string &str, const char c){
auto size = str.size();
for(decltype(size) i = 0; i < size; i++){
if(str[i] == c){
return true;
}
}
}
看是有return语句,但是只有循环中满足特定条件才能正常返回,如果找不到的话实际上是没有返回值。编译器可能能检测出这个错误,也可能检测不出来,要看编译器的实现,好在大部分情况编译器甚至IDE可以帮我们检测出来,但是如果不幸我们用了检测不出来的编译器,可能会在运行时发生未定义行为错误。
2. 理解值是如何被返回的
类似于形参被初始化,函数返回一个值也是类似于变量初始化。最终是返回的值初始化调用点的一个临时量,这个临时量就是函数调用的结果。
如果函数返回的是局部变量,则返回值将被拷贝到调用点。
如果函数返回的是引用,因为引用只是它所引对象的别名,则不会将所引用的值拷贝到调用点。
那么问题来了,如果返回的引用引用了局部变量会发生什么?
答案是会发生错误,因为返回了未定义的值。
因为在方法体内,函数执行完成意味着局部变量存储的空间会被释放,局部变量的引用指向了非法内存区域。
举个栗子:
const string *getStr(){
string ret;
return ret; //返回局部对象的引用
//return “Hello”; //Hello是局部临时变量
}
如果函数体定义的是const string getStr()
那么这么实现一点问题都没有,因为返回的是对象本身,会进行一次拷贝。
返回局部对象的指针也是类似的问题。
3. 返回类类型的函数
我们经常有看到有这样使用的场景:
std::vector<std:string> strs;
strs.begin().size();
当函数返回的是类类型,因为它的返回值可以继续参与运算,所以使用调用运算符可以继续调用函数返回结果对象的成员。
这个我们日常开发中并不少见。
4. 返回左值
如果函数返回的是引用的左值,那么我们可以为函数结果赋值:
int &getVal(int &i){
return i;
}
void main(){
int value = 100;
getVal(value) = 200;
}
这里面看是怪怪的,但是其实是正确的。因为返回值是引用,所以调用是个左值,左值就是可以赋值。
但是要注意,如果返回的是常量引用,那么我们就不能再这么赋值了。
5. 返回列表
C++11中允许我们使用花括号包围的值的列表当返回值,初始化临时的vector:
std::vector<int> getValues(){
return {1, 2,3,4};
}
不再需要我们手动去定义vector变量并初始化后再返回。
6. 返回数组指针
数组有个特性就是不能被拷贝,那么如果我们返回的是数组怎么办?怎么把返回值拷贝到调用点呢?
所以函数不能返回数组,但是可以返回数组指针或引用。
我们怎么定义一个返回数组指针或者引用的函数呢?有个比较简单的办法,就是使用类型别名:
typedef int arrT[10];//等价于using arrT = int[10];
arrT* fun(int i);
arrT是含有10个整数的数组的别名。
7. 总结
文本介绍了函数返回值的各种小细节:值是如何被返回,返回类类型怎么使用,返回左值引用,返回列表以及返回数组指针等。
最后,如果大伙有什么好的学习方法或建议欢迎大家在评论中积极留言哈,希望大家能够共同学习、共同努力、共同进步。
小编在这里祝小伙伴们在未来的日子里都可以 升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰!!
不论遇到什么困难,都不应该成为我们放弃的理由!
很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,需要一份小编整理出来的学习资料的关注我主页或者点击扫描下方二维码免费领取~
这里是关于我自己的Android 学习,面试文档,视频收集大整理,有兴趣的伙伴们可以看看~
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。