Android C++系列:C++11函数特殊特性

简介: 在Python中函数有默认参数等,在C++11中我们发现C++也支持了默认参数;还有C++特有的内联函数、constexpr函数等知识都有不少细节,本文对这些知识做详细介绍。

image.png


1. 背景


在Python中函数有默认参数等,在C++11中我们发现C++也支持了默认参数;还有C++特有的内联函数、constexpr函数等知识都有不少细节,本文对这些知识做详细介绍。


2. 默认参数


我们在Java中重载函数时经常有这样的形式:


public CustomView(Context context) {
    super(context);
  }
  public CustomView(Context context,
      @Nullable AttributeSet attrs) {
    super(context, attrs);
    init(context, attrs);
  }
  public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs);
  }


同一个函数因为参数个数不同最后定义了好几种形式,有些场景我们可以这样理解,参数较少的函数只是参数较多的函数的某个参数是个特定值,比如上面第二个构造函数可以理解成第一个构造函数的defStyleAttr为0,我们不去使用这个参数。这样就面临一个问题,明明一个函数可以解决的事情,最后搞出好几个函数来,有没有一种简便方法呢?在Python中我们用了默认参数实现,C++11也提供了这种特性。


某些函数有这样一种形参,在函数的很多次调用中它们都被赋予一个相同的值,此时,我们把这个反复出现的值称为函数的默认实参(default argument)。调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。


实际的代码工程中经常看到这种(随便从工程代码截取一段):


std::future<bool> notifyOfWakeWord(
        capabilityAgents::aip::AudioProvider wakeWordAudioProvider,
        avsCommon::avs::AudioInputStream::Index beginIndex,
        avsCommon::avs::AudioInputStream::Index endIndex,
        std::string keyword,
        std::chrono::steady_clock::time_point startOfSpeechTimestamp,
        std::shared_ptr<const std::vector<char>> KWDMetadata = nullptr) override;
    std::future<bool> notifyOfTapToTalk(
        capabilityAgents::aip::AudioProvider tapToTalkAudioProvider,
        avsCommon::avs::AudioInputStream::Index beginIndex = capabilityAgents::aip::AudioInputProcessor::INVALID_INDEX,
        std::chrono::steady_clock::time_point startOfSpeechTimestamp = std::chrono::steady_clock::now(),
        capabilityAgents::aip::Initiator initiator = capabilityAgents::aip::Initiator::TAP,
        std::string keyword = "",
        float wakeUpConfidence = 0.f) override;


再举个例子说明:


void setPosition(int x, int y, int z = 0);


我们设置坐标值,如果是二维空间z坐标都为0,这时候我们只需要这样调用:


setPosition(1,1);


如果是三维需要设置z坐标:


setPosition(1,1,1);


我们可以定义一个或者多个形参的默认值,但是要注意:


  • 一旦一个形参被赋予了默认值,它后面的所有形参都必须有默认值,换句话说就是默认参数必须放在参数列表的最后面。
  • 调用函数时,其中一个参数使用了默认值,这个参数对应形参里面后面的所有参数都必须使用默认值。


**最佳实现:**设计含有默认实参的函数时,尽量让不怎么用到默认值的形参出现在前面,让经常使用默认值的形参出现在后面。


对于函数的声明来说,我们的习惯是将其放在头文件中,并且一个函数只声明一次,但是C++中多次声明同一个函数也是合法的。我们在给定的作用域中一个形参只能被赋予一次默认实参。意味着,函数的后续声明只能为之前那些没有默认值的形参添加默认实参,而且该形参右侧的所有形参必须都有默认值


通常我们使用的默认实参都是字面值,其实只要表达式的类型能转换成形参所需的类型,这个表达式就能作为默认实参。注意:局部变量不能作为默认实参。


int dz= 100;
void setPosition(int x, int y, int z = dz);


用作默认实参的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数调用时:


int dz = 100;
void setPosition(int x,int y int z = dz);
void fun(){
  dz = 200;
  setPosition(100,100);
}


我们在fun内部改变了dz值,所以调用setPosition时传递的是更新过的值。


3. 内联函数


我们一般的函数调用会有以下动作:


  1. 调用前先保存寄存器;
  2. 在返回时恢复;
  3. 如果需要会拷贝实参
  4. 程序转向一个新的位置继续执行。


总结下来就是函数调用是有不小开销的。有没有办法优化这块呢?答案是内联函数


内联函数实际就是在每个调用点上”内联的“展开。


比如我们定义了一个内联函数:


inline int min(int x , int y){
  return x > y ? y : x;
}


调用函数:


std::cout << min(1,2) << std::end;


再编译过程会展开成下面形式:


std::cout << (x > y ? y :x) << std::endl;


上面也看到了,内联函数的定义是在函数返回类型前面加上关键字inline


内联关键字是想编译器发出请求,编译器具体实现时也可以忽略这个请求。


内联机制用于优化规模较小,流程直接,频繁调用的函数。对于大部分编译器,不支持内联递归函数,对于75行的函数也大概不允许在调用节点内联展开。


4. constexpr函数


constexpr函数是指能用于常量表达式的函数。定义constexpr函数的方法与其他函数类似,不过要遵循以下几项规定:


  1. 函数的返回类型及所有形参的类型都得是字面值类型
  2. 函数体中必须有且只有一条return语句。


constexpr int getX(){
  return 20;
}
constexpr int x = getX();


执行初始化任务时,编译器把对constexpr函数的调用替换成其结果值。和inline一样都是在编译时,所以为了能在编译过程中随时展开,constexpr函数被隐式地指定为内联函数。


constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。例如,constexpr函数中可以有空语句、类型别名以及using声明。


最佳实践:


内联函数和constexpr函数可以在程序中多次定义。因为编译器要想展开函数仅有函数声明是不够的,还需要函数的定义。但是对于某个给定的内联函数或者constexpr函数来说,它的多个定义必须完全一致。基于这个原因,内联函数和constexpr函数通常定义在头文件中。


5. 总结


本文介绍了C++函数的特殊语法:默认参数、内联函数、constexpr函数。其中默认参数是C++11新特性,constexpr默认也是内联的,内联是在调用节点展开,有点类似宏展开。

目录
相关文章
|
5天前
|
C语言 C++ 开发者
深入探索C++:特性、代码实践及流程图解析
深入探索C++:特性、代码实践及流程图解析
|
8天前
|
Linux Android开发
测试程序之提供ioctl函数应用操作GPIO适用于Linux/Android
测试程序之提供ioctl函数应用操作GPIO适用于Linux/Android
12 0
|
1天前
|
存储 程序员 C语言
深入理解C++:从语言特性到实践应用
深入理解C++:从语言特性到实践应用
11 3
|
2天前
|
编译器 C++
【C++进阶】引用 & 函数提高
【C++进阶】引用 & 函数提高
|
6天前
|
C++
C++从入门到精通:2.1.2函数和类——深入学习面向对象的编程基础
C++从入门到精通:2.1.2函数和类——深入学习面向对象的编程基础
|
6天前
|
存储 C++
C++从入门到精通:2.1.1函数和类
C++从入门到精通:2.1.1函数和类
|
7天前
|
设计模式 安全 编译器
【代码片段】【C++】C++11线程安全单例模式
【代码片段】【C++】C++11线程安全单例模式
13 1
|
7天前
|
Android开发
Android构建系统:Android.mk(2)函数详解
Android构建系统:Android.mk(2)函数详解
15 1
|
7天前
|
移动开发 API 数据处理
构建高效安卓应用:探究Android 12中的新特性与性能优化策略
【4月更文挑战第23天】 随着移动设备的普及,用户对应用程序的性能和效率要求越来越高。安卓系统作为市场占有率最高的移动操作系统之一,其版本更新带来了众多性能提升和新特性。本文将深入探讨Android 12版本中引入的关键性能优化技术,并分析这些技术如何帮助开发者构建更加高效的安卓应用。我们将从最新的运行时权限、后台任务优化、以及电池使用效率等方面入手,提供具体的实践建议,旨在帮助开发者更好地利用这些新工具,以提升应用的响应速度、降低能耗,并最终提高用户的满意度。
|
14天前
|
机器学习/深度学习 定位技术 C++
c++中常用库函数
c++中常用库函数
38 0