• 特殊用途语言特性(默认实参/内联函数/constexpr函数/assert预处理宏/NDEBUG预处理变量)


    默认实参:

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

    需要特别注意的是:

    1. 一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。

    2. 只能省略尾部的实参。

    3. 实参会从形参列表的第一个形参开始匹配,若它们的类型能相互转换也能匹配成功。

    4. 局部变量不能作为默认实参。除此之外,只要表达式的类型能转换成形参所需要的类型,该表达式就能作为默认实参。

     1 #include <iostream>
     2 using namespace std;
     3 
     4 int gel(int a = 1, int b = 1, int c = 1){
     5     return a + b + c;
     6 }
     7 
     8 int ting(int a = 1, char b = 'a', string c = "fjslf"){
     9     return a;
    10 }
    11 
    12 int yy1 = 1, yy2 = 1;
    13 
    14 int yu(int a = yy1, int b = yy2){//可以使用全局变量作默认实参
    15     return a + b;
    16 }
    17 
    18 // int lou(int a = 1, int b, int c){//错误:一旦某个形参有默认值,则其后面的所有形参都必须有默认值
    19 //     ;
    20 // }
    21 
    22 int main(void){
    23     int cnt1 = gel();
    24     int cnt2 = gel(2);
    25     int cnt3 = gel(2, 2);
    26     int cnt4 = gel(2, 2, 2);
    27     cout << cnt1 << " " << cnt2 << " " << cnt3 << " " << cnt4 << endl;//输出 3 4 5 6
    28 
    29     int cc1 = ting(10, 'a', "fskl");
    30     // int cc2 = ting('a', "lsjf");//错误:只能省略尾部实参
    31     int cc3 = ting(2, 'b');
    32     int cc4 = ting('b');//注意'b'能转换成int类型,此时'b'是赋值给形参a的
    33     cout << cc1 << " " << cc3 << " " << cc4 << endl;//输出10 2 98
    34     return 0;
    35 }

    内联函数:

    内联函数可以避免函数调用时的时间开销。将函数指定为内联函数,通常就是将它在调用点上“内联地”展开。相当于将该函数直接嵌入调用点。

    定义一个比较两个 int 型的内联函数:

    1 inline bool compre(const int a, const int b){
    2     return a > b;
    3 }

    内联说明只是向编译器发出的一个请求,编译器可以选择忽略这个请求。一般来说,内联机制用于优化规模比较小,流程直接,频繁调用的函数。很多编译器都不支持内联递归函数,而且一个 75 行的函数也不大可能在调用点内联地展开。

    constexpr 函数:

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

    1. 函数的返回类型以及所有的形参类型都得是字面值类型。

    2. 函数体内必须有且只有一条 return 语句。

     1 #include <iostream>
     2 using namespace std;
     3 
     4 constexpr int new_sz(int a){
     5     return a;
     6 }
     7 
     8 constexpr char new_char(char s){
     9     return s;
    10 }
    11 
    12 // constexpr string new_str(string s){//错误
    13 //     return s;
    14 // }
    15 
    16 int main(void){
    17     const int foo = new_sz(10);
    18     cout << foo << endl;
    19 
    20     const char ch = new_char('h');
    21     cout << ch << endl;
    22     return 0;
    23 }

    指针和引用也是字面值类型:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 constexpr int* new_sz(int &a){
     5     return &a;
     6 }
     7 
     8 constexpr int* get(int *a){
     9     return a;
    10 }
    11 
    12 int main(void){
    13     int b = 1024;
    14     int *a = new_sz(b);
    15     cout << *a << endl;//输出1024
    16 
    17     int *c = get(&b);
    18     cout << *c << endl;//输出1024
    19     return 0;
    20 }

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

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

    1 constexpr int new_sz(int a){
    2     // int b;错误:在constexpr函数中不能有执行操作的语句
    3     using ll = long long;//在constexpr函数中可以有using声明
    4     ;//在constexpr函数中可以有空语句
    5     typedef int in;//在constexpr函数中可以有命名类型别名
    6     return a;
    7 }

    和其他函数不一样,内联函数和 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 头文件里的源码可以发现:

     1 /* 
     2  * assert.h
     3  * This file has no copyright assigned and is placed in the Public Domain.
     4  * This file is a part of the mingw-runtime package.
     5  * No warranty is given; refer to the file DISCLAIMER within the package.
     6  *
     7  * Define the assert macro for debug output.
     8  *
     9  */
    10 
    11 /* We should be able to include this file multiple times to allow the assert
    12    macro to be enabled/disabled for different parts of code.  So don't add a
    13    header guard.  */ 
    14 
    15 #ifndef RC_INVOKED
    16 
    17 /* All the headers include this file. */
    18 #include <_mingw.h>
    19 
    20 #undef assert
    21 
    22 #ifdef    __cplusplus
    23 extern "C" {
    24 #endif
    25 
    26 #ifdef NDEBUG
    27 /*
    28  * If not debugging, assert does nothing.
    29  */
    30 #define assert(x)    ((void)0)
    31 
    32 #else /* debugging enabled */
    33 
    34 /*
    35  * CRTDLL nicely supplies a function which does the actual output and
    36  * call to abort.
    37  */
    38 _CRTIMP void __cdecl __MINGW_NOTHROW _assert (const char*, const char*, int) __MINGW_ATTRIB_NORETURN;
    39 
    40 /*
    41  * Definition of the assert macro.
    42  */
    43 #define assert(e)       ((e) ? (void)0 : _assert(#e, __FILE__, __LINE__))
    44 
    45 #endif    /* NDEBUG */
    46 
    47 #ifdef    __cplusplus
    48 }
    49 #endif
    50 
    51 #endif /* Not RC_INVOKED */

    用 NDEBUG 宏避免 assert 检查:

     1 #include <iostream>
     2 #define NDEBUG//注意NDEBUG宏一定要定义在cassert头文件引用前,不然#include <cassert>句执行的时候
     3 // 就生成了在没有定义NDEBUG情况下的代码,后面定义的NDEBUG不会影响assert的行为
     4 #include <cassert>
     5 using namespace std;
     6 
     7 int main(void){
     8     assert(0);
     9     cout << 1 << endl;
    10     return 0;
    11 }

    预处理器定义的几个对于程序调试很有用的名字:

    1 __func__ // 存储当前函数的名字, 是 const char 的一个静态数组
    2 __FILE__ // 存放当前文件名字符串字面值
    3 __LINE__ // 存放当前行号的整型字面值
    4 __TIME__ // 存放文件编译时间的字符串字面值(时分秒)
    5 __DATE__ // 存放文件编译日期的字符串字面值(月日年)
     1 #include <iostream>
     2 using namespace std;
     3 
     4 void gel(void){
     5     cout << __func__ << endl;
     6     cout << __FILE__ << endl;
     7     cout << __LINE__ << endl;
     8     cout << __TIME__ << endl;
     9     cout << __DATE__ << endl;
    10 }
    11 
    12 int main(void){
    13     gel();
    14     return 0;
    15 }

  • 相关阅读:
    Cogs 452. Nim游戏!(博弈)
    Cogs 876. 游戏(DP)
    Cogs 2546. 取石块儿(博弈)
    Bzoj 4147: [AMPPZ2014]Euclidean Nim(博弈)
    Codevs 3002 石子归并 3(DP四边形不等式优化)
    洛谷 P1041 传染病控制
    洛谷 P1967 货车运输
    洛谷 P1038 神经网络
    洛谷 P1027 Car的旅行路线
    洛谷 P1054 等价表达式
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/8052965.html
Copyright © 2020-2023  润新知