特殊用途语言特性(默认实参/内联函数/constexpr函数/assert预处理宏/NDEBUG预处理变量)
默认实参:
某些函数有这样一种形参,在函数的很多次调用中它们都被赋予一个相同的值,此时,我们把这个反复出现的值称为函数的默认实参。调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。
需要特别注意的是:
1. 一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。
2. 只能省略尾部的实参。
3. 实参会从形参列表的第一个形参开始匹配,若它们的类型能相互转换也能匹配成功。
4. 局部变量不能作为默认实参。除此之外,只要表达式的类型能转换成形参所需要的类型,该表达式就能作为默认实参。
#include <iostream>
using namespace std; int gel(int a = , int b = , int c = ){
return a + b + c;
} int ting(int a = , char b = 'a', string c = "fjslf"){
return a;
} int yy1 = , yy2 = ; int yu(int a = yy1, int b = yy2){//可以使用全局变量作默认实参
return a + b;
} // int lou(int a = 1, int b, int c){//错误:一旦某个形参有默认值,则其后面的所有形参都必须有默认值
// ;
// } int main(void){
int cnt1 = gel();
int cnt2 = gel();
int cnt3 = gel(, );
int cnt4 = gel(, , );
cout << cnt1 << " " << cnt2 << " " << cnt3 << " " << cnt4 << endl;//输出 3 4 5 6 int cc1 = ting(, 'a', "fskl");
// int cc2 = ting('a', "lsjf");//错误:只能省略尾部实参
int cc3 = ting(, 'b');
int cc4 = ting('b');//注意'b'能转换成int类型,此时'b'是赋值给形参a的
cout << cc1 << " " << cc3 << " " << cc4 << endl;//输出10 2 98
return ;
}
内联函数:
内联函数可以避免函数调用时的时间开销。将函数指定为内联函数,通常就是将它在调用点上“内联地”展开。相当于将该函数直接嵌入调用点。
定义一个比较两个 int 型的内联函数:
inline bool compre(const int a, const int b){
return a > b;
}
内联说明只是向编译器发出的一个请求,编译器可以选择忽略这个请求。一般来说,内联机制用于优化规模比较小,流程直接,频繁调用的函数。很多编译器都不支持内联递归函数,而且一个 75 行的函数也不大可能在调用点内联地展开。
constexpr 函数:
constexpr 函数是指能用于常量表达式的函数。定义 constexpr 函数的方法与其他函数类型,不过要遵循几项约定:
1. 函数的返回类型以及所有的形参类型都得是字面值类型。
2. 函数体内必须有且只有一条 return 语句。
#include <iostream>
using namespace std; constexpr int new_sz(int a){
return a;
} constexpr char new_char(char s){
return s;
} // constexpr string new_str(string s){//错误
// return s;
// } int main(void){
const int foo = new_sz();
cout << foo << endl; const char ch = new_char('h');
cout << ch << endl;
return ;
}
指针和引用也是字面值类型:
#include <iostream>
using namespace std; constexpr int* new_sz(int &a){
return &a;
} constexpr int* get(int *a){
return a;
} int main(void){
int b = ;
int *a = new_sz(b);
cout << *a << endl;//输出1024 int *c = get(&b);
cout << *c << endl;//输出1024
return ;
}
执行初始化任务时,编译器把 constexpr 函数的调用替换成其结果值。为了能在编译过程中随时展开,constexpr 函数被隐式的指定为内联函数。
constexpr 函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。例如:constexpr 函数中可以有空语句,类型别名以及 using 声明:
constexpr int new_sz(int a){
// int b;错误:在constexpr函数中不能有执行操作的语句
using ll = long long;//在constexpr函数中可以有using声明
;//在constexpr函数中可以有空语句
typedef int in;//在constexpr函数中可以有命名类型别名
return a;
}
和其他函数不一样,内联函数和 constexpr 函数可以在程序中多次定义。毕竟,编译器要想展开函数仅有声明是不够的,还需要函数的定义。不过,对于某个给定的内联函数或者 constexpr 函数来说,它的多个定义必须完全一致。基于这个原因,内联函数和 constexpr 函数通常定义在头文件中。
调试帮助
assert 预处理宏:
assert 是一种预处理宏。所谓预处理宏其实是一个预处理变量,它的行为有点类似于内联函数。assert 宏使用一个表达式作为它的条件:
assert(expr);
首先对 expr 求值,如果表达式为假,assert 输出信息并终止程序执行。如果表达式为真,assert 什么也不做。assert 宏定义在 cassert 头文件中。如我们所知,预处理名字由预处理器而非编译器管理,因此我们可以直接使用预处理名字而无需提供 using 声明。也就是说,我们应该使用 assert 而不是 std::assert,也不需要为 assert 提供 using 声明。
和预处理变量一样,宏名字在程序内必须唯一。含有 cassert 头文件的程序不能再定义为 assert 的变量,函数或者其他实体。在实际编程中,即使没有包含 cassert 头文件,也最好不要为了其他目的使用 assrert。因为其他头文件中也有可能包含了 cassert。
assert 宏常用于检查“不能发生”的条件。
NDEBUG 预处理变量:
assert 的行为依赖于一个名为 NDEBUG 的预处理变量的状态。如果定义了 NDEBUG 则 assert 什么也不做。默认状态下没有定义 NDEBUG,此时 assert 将执行运行时检查。这些通过查看 assert.h 头文件里的源码可以发现:
/*
* assert.h
* This file has no copyright assigned and is placed in the Public Domain.
* This file is a part of the mingw-runtime package.
* No warranty is given; refer to the file DISCLAIMER within the package.
*
* Define the assert macro for debug output.
*
*/ /* We should be able to include this file multiple times to allow the assert
macro to be enabled/disabled for different parts of code. So don't add a
header guard. */ #ifndef RC_INVOKED /* All the headers include this file. */
#include <_mingw.h> #undef assert #ifdef __cplusplus
extern "C" {
#endif #ifdef NDEBUG
/*
* If not debugging, assert does nothing.
*/
#define assert(x) ((void)0) #else /* debugging enabled */ /*
* CRTDLL nicely supplies a function which does the actual output and
* call to abort.
*/
_CRTIMP void __cdecl __MINGW_NOTHROW _assert (const char*, const char*, int) __MINGW_ATTRIB_NORETURN; /*
* Definition of the assert macro.
*/
#define assert(e) ((e) ? (void)0 : _assert(#e, __FILE__, __LINE__)) #endif /* NDEBUG */ #ifdef __cplusplus
}
#endif #endif /* Not RC_INVOKED */
用 NDEBUG 宏避免 assert 检查:
#include <iostream>
#define NDEBUG//注意NDEBUG宏一定要定义在cassert头文件引用前,不然#include <cassert>句执行的时候
// 就生成了在没有定义NDEBUG情况下的代码,后面定义的NDEBUG不会影响assert的行为
#include <cassert>
using namespace std; int main(void){
assert();
cout << << endl;
return ;
}
预处理器定义的几个对于程序调试很有用的名字:
__func__ // 存储当前函数的名字, 是 const char 的一个静态数组
__FILE__ // 存放当前文件名字符串字面值
__LINE__ // 存放当前行号的整型字面值
__TIME__ // 存放文件编译时间的字符串字面值(时分秒)
__DATE__ // 存放文件编译日期的字符串字面值(月日年)
#include <iostream>
using namespace std; void gel(void){
cout << __func__ << endl;
cout << __FILE__ << endl;
cout << __LINE__ << endl;
cout << __TIME__ << endl;
cout << __DATE__ << endl;
} int main(void){
gel();
return ;
}
特殊用途语言特性(默认实参/内联函数/constexpr函数/assert预处理宏/NDEBUG预处理变量)的更多相关文章
- 特殊用途语言特性——默认参数、内联函数和constexptr函数
1 默认实参 某些函数有这样一些参数,在函数的很多次调用中它们都被赋予一个相同的值,此时,我们把这个反复出现的值称为函数的默认实参.调用含有默认实参的函数时,可以包含该实参,也可以省略该实参. 我们可 ...
- 【c++ primer, 5e】特殊用途语言特性
[默认实参] 1.注意点:函数的默认实参可以在函数的声明中添加,但是后续声明只能添加默认参数而不能改变先前声明的默认参数.(函数的声明通常是定义在头文件上的,多次声明同一个函数是合法的) 2.默认实参 ...
- C++语言基础(7)-inline内联函数
函数调用是有时间和空间开销的.程序在执行一个函数之前需要做一些准备工作,要将实参.局部变量.返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码:函数体中的代码执行完毕后还要清理现场,将之前压 ...
- Oracle的分页查询及内联视图和函数处理
1.Oracle的分页常用方式: select * from(select * ,ROWNUM num from table where ROWNUM<=20 ) where num>0; ...
- 内存四个领域,变量声明和定义,注册,c内联汇编,auto,堆,不变,静态变量
1.内存四大区域 2.在程序中,变量的声明能够有多份,定义仅仅能有一份 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdG90b3R1enVvcXVh ...
- c++中函数的参数传递,内联函数和默认实参的理解
1.参数传递 1)函数调用时,c++中有三种传递方法:值传递.指针传递.引用传递. 给函数传递参数,遵循变量初始化规则.非引用类型的形参一相应的实参的副本初始化.对(非引用)形参的任何修改仅作用域局部 ...
- 我的c++学习(6)默认参数和内联函数
默认参数 一般情况下,函数调用时实参个数应与形参相同,但为了更方便地使用函数,C++也允许定义具有默认参数的函数,这种函数调用时实参个数可以与形参不相同.“默认参数”指在定义或声明函数时为形参指定默认 ...
- C++初阶(命名空间+缺省参数+const总结+引用总结+内联函数+auto关键字)
命名空间 概述 在C/C++中,变量.函数和后面要学到的类都是大量存在的,这些变量.函数和类的名称将都存在于全局作用域中,可能会导致很多冲突.使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲 ...
- Lua 函数参数 & 默认实参
[1]Lua函数,默认实参 习惯了其他语言(如C++)的默认实参,利用Lua语言的过程中,发现没有默认实参这种机制. 所以,自己模拟了一个满足业务需求的带默认实参的函数. (1)示例如下: local ...
随机推荐
- 双杀 0day 漏洞(CVE-2018-8174)复现
漏洞描述: CVE-2018-8174 是 Windows VBScript Engine 代码执行漏洞. 微软在4月20日早上确认此漏洞,并于5月8号发布了官方安全补丁,对该 0day 漏洞进行了修 ...
- Deep Learning 学习笔记(7):神经网络的求解 与 反向传播算法(Back Propagation)
反向传播算法(Back Propagation): 引言: 在逻辑回归中,我们使用梯度下降法求参数方程的最优解. 这种方法在神经网络中并不能直接使用, 因为神经网络有多层参数(最少两层),(?为何不能 ...
- ndnarry矩阵处理
ndarray的矩阵运算 数组是编程中的概念,矩阵.矢量是数学概念. 在计算机编程中,矩阵可以用数组形式定义,矢量可以用结构定义! 1. 矢量运算:相同大小的数组间运算应用在元素上 示例代码(1): ...
- 微信公众平台PHP示例一
<?php /** * Created by PhpStorm. * User: Administrator * Date: 2015-12-18 * Time: 21:51 */ define ...
- Java的单向加密算法MD5和SHA——加密和解密
出自:http://www.cnblogs.com/onetwo/p/3875551.html 1.JDK中MD5和SHA加密的主要类 在JDK6.0中,与MD5与SHA密切相关的几个类的类图如下: ...
- mahout in Action2.2-给用户推荐图书(2)-分析对用户推荐书目的结果
2.2.3 Analyzing the output 在之前的程序运行结果中我们得到的结果输出是: RecommendedItem [item:104, value:4.257081] 程序要求选择一 ...
- Java多线程-新特征-阻塞栈LinkedBlockingDeque
对于阻塞栈,与阻塞队列相似.不同点在于栈是“后入先出”的结构,每次操作的是栈顶,而队列是“先进先出”的结构,每次操作的是队列头. 这里要特别说明一点的是,阻塞栈是Java6的新特征.. Java为阻塞 ...
- MFC小程序
1.将菜单栏归零,工具栏放在窗口低部,加载自己新建的工具栏 CMainFrame::OnCreate()函数中 this->SetMenu(0); 2.将窗口初始化为最大化 APP类中:m_pM ...
- Redis搭建(二):主从复制
一.引言 Redis有三种集群模式: 第一个就是主从模式 第二种“哨兵”模式,在Redis 2.6版本开始提供,2.8版本稳定 第三种是Cluster集群模式,在Redis 3.x以后的版本才增加进来 ...
- java基础之JDBC九:DbUtils的简介及使用
DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能. 使用步骤: A: 获取可以执行SQL语句的对象. pu ...