Android C++系列:函数返回值注意事项

简介: 函数返回值就是使用return语句终止正在执行的函数,看是很简单的问题有什么说的呢?因为越是简单的问题里面越是有一些不易发现的坑。

image.png


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. 总结


文本介绍了函数返回值的各种小细节:值是如何被返回,返回类类型怎么使用,返回左值引用,返回列表以及返回数组指针等。

目录
相关文章
|
1月前
|
存储 安全 编译器
给我介绍一些C++中引用的使用注意事项
C++引用是变量别名,简洁但易踩坑:必须初始化且不可重绑定;非const引用不能绑定右值;避免返回局部变量引用;不存在引用的数组或指针;注意const引用延长临时对象生命周期;区分引用声明与取地址符&。正确使用可提升代码安全与可读性。
139 5
|
5月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
156 0
|
8月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
474 6
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
492 6
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
291 0
C++ 多线程之线程管理函数
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
207 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
9月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
5月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
248 0
|
7月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
283 12

热门文章

最新文章

下一篇
oss云网关配置