美文网首页
11-12 逻辑 位 递增 / 自由存储 / lambda /

11-12 逻辑 位 递增 / 自由存储 / lambda /

作者: my_passion | 来源:发表于2022-06-21 19:14 被阅读0次

    第11章 选择适当的 操作

    11.1 逻辑 / 位 / 递增减

    1 逻辑

        && || !
        
        (1) 操作数
        
            指针/算术 类型
        
        (2) 短路特性: 逻辑上 需要 时, && 和 || 才会对 第2实参求值
        
            while (p && !whitespace(*p) ) ++p;
    

    2 位

        & | ~(非) ^(异或) >> <<
        
        含义 & 应用(位向量)
            
                unsigned 整型 
                    每1位 表示 集合的1个成员
    
        (1) & 求 交 
                
                从 32 位 int 中 提前中间 16位
                
                constexpr unsigned short middle(int n)
                {
                    static_assert( sizeof(int) == 4, "unexpected int size" );
                    static_assert( sizeof(short) == 2, "unexpected short size" );
                    
                    return (n >> 8) & 0xFFFF;
                }
                
                int x = 0xFF00FF00;  // 假定 sizeof(int) = 4
                short y = middle(x); // y = 0x00FF
                
        (2) | 求 并
            
            在 现有状态 上 添加内容
                
                enum ios_base::iostate
                {
                    goodbit = 0, eofbit = 1, failbit = 2, badbit = 4
                };
                
                state |= eofbit;
        
        (3) ~ 求 补
            
        (4) ^ 异或: 相异( 有变化 ) 为 1
                 
            判 `状态 是/否 变化`
            
                现值 与 旧值 异或 => result 作 条件 转换为 `bool 值 true/false` 
                
                if ( 结果 可转换为 true <=> 至少有 1 位 有变化 => 有变化 )
            
                int old = cin.rdstate(); // rdstate() 返回 状态
                // ...
                if( cin.rdstate() ^ old ) // 有变化吗 ? 
    

    3 递 增/减

        (1) char* strcpy(char*, const char*); // <string.h>
        
            循环 拷贝 以 0 结尾的 C 风格字符串
        
            void cpy(char* p, const char* q);
            
            6 个版本
            
            1) `读 2 次 字符串`
                
                先求 strlen(): 第 1 次 读
                
                int length = strlen(q);
                for(int i = 0; i <= length; i++)
                    p[i] = q[i];
                    
                |
                |   性能提升
                |/
                
            2) `读 1 次 字符串`
                
                没遇到 结束符 0 时, copy
                
                + 
                
                遇到时, 跳出 + dst 置 结束符 0
                
                for(int i = 0; q[i] != 0; i++)
                    p[i] = q[i];
                p[i] = 0; 
            
            3) 用 `指针操作 替换 数组下标操作`
            
                while (*q != 0)
                {
                    *p = *q;
                    p++;
                    q++;
                }
                *p = 0;
            
            4) `后置递增: 允许 先使用值 再 递增`
                
                while(*q != 0)
                    **p++ = *q++;
                *p = 0;
            
            5) `赋值表达式 的 值` 是 右操作数的值/赋值后左操作数 的值 
            
                *p++ = *q++ 的值是 *q
                
                => while(*p++ = *q++) 终止条件是 *q == 0
                
                    1> copy: *p = *q -> 结果 *q 转换为 bool 值 -> 编译器 先存着
                    2> 递增
                    3> 判 递增前 *q (若 0) 转换的 bool 值 -> 若 false -> exit => 结束符 已 copy 给 dst
            
                while(*p++ = *q++)
                {}
            
            6) 循环体空 => 略去
            
                while(*p++ = *q++);
    

    11.2 自由存储

    (1) 引入
    
        命名对象 lifetime 由其 scope 决定
                |
                |   希望 `分离 对象 与` 对象创建语句所在 `scope`
                |/
            new 运算符
                分配的对象 `位于 自由存储 之上`
        
    (2) delete 运算符
    
        1) delete / delete[] nullptr; 什么也不做
                
        2) 被 delete 的对象类型 含 dtor
        
            delete 将 
                
            [1] 调用 该 dtor 
                
                释放 该对象 所管理的资源 (若有)
                
            [2] 释放 该对象所占内存空间 
                
                以供后续使用
    

    1 内存管理

        (1) 自由存储 的 问题
        
            1) 对象泄露
                
                new 但忘了 delete ---> 可能资源耗尽
                
            2) 过早 delete
                
            3) 重复 delete
                
                同一对象被释放 2 次
                    2 次调用 dtor      
        
        (2) 解决
        
            1) 优先`scoped 对象`
                
                必要时再 自由存储
    
            2) RAII  
            
                [1] 将 自由存储 对象 的 `ptr` 放到 `handle 对象` 中 
                                                |
                                                |   尽可能 作 
                                                |/
                                            `scope 内 变量`
                 
                    string / vector / unique_ptr / shared_ptr
                
                [2] 很多用 自由存储 的场合
                
                    都可以用 `move 语义` 替代
                    
                        从 函数返回 表示 大对象的 句柄 即可
                    
                RAII
                    字面含义
                        句柄 ctor 中 创建资源, dtor 中 释放资源
                        
                    本质含义
                        让 句柄 own 资源, dtor 中 释放资源 
                        
                        =>
                        `资源 与 句柄 的 lifetime 管理 统一起来`
                        
                        只要不泄露句柄, 就能不泄露资源
                        
                        至于让 ctor 创建资源, 还是 把资源创建好, 再交给 ctor 保管, 无优劣之分
                        
                        可用 Factory Method 返回 创建好的资源, client 就不用管 里面怎么实现了
    

    2 除非必须直接使用 char*, 否则一般而言, std::string 更好

        (1) new 创建 对象的数组
        
            char* save_string(const char* p)
            {
                char* s = new char[strlen(p) + 1];
                strcpy(s, p);
                return s;
            }
            
            int main(int argc, char* argv[])
            {
                // ...
                char* p = save_string( argv[1] );
                // ...
                delete[] p;
            }
                |
                |   (2) 
                |       
                |       简化 + 不操心 delete / delete[]
                |/
            string save_string(const char* p)
            {
                return string{p};
            }
            int main(int argc, char* argv[])
            {
                // ...
                string s = save_string( argv[1] );
                // ...
            }
            
        (2) delete / delete[] 必须清楚 分配的对象 有多大
        
            new 分配的对象 比 静态对象 所占空间 稍大(至少存得下 对象 size)
    

    3 重载 new

        默认下, new 运算符 在 自由存储 上 创建对象
            |
            |/
        想在 `指定空间` 分配对象
                |
                |   重载 new: placement new <new>
                |/
        目标空间地址 作 参数 
            
        void* buf = reinterpret_cast<void*>(0xF00F); // 明确的地址
        
        void* operator new(size_t, void* p) { return p; } // placement new <new>
                            /            |\
                     _ _ _ /_ _ _ _ _ _ _|
                    |     / 
                    |/   /
        X* p = new(buf) X; // 在 buf 处 构建 X, 
                           // 调 operator new( sizeof(X), buf )
        // ===
        clas B
        {
        public:
            virtual void* alloc(size_t) = 0;
            virtual void free(void*) = 0;
            // ...
        };
        
        void* operator new(size_t sz, B* pB)
        {
            return pB->alloc(sz);
        }
        
        extern B* gpPersistent;
        extern B* gpShared;
        
        void g(int n)
        {
            X* p = new(gpPersistent) X(i); // 在 持续存储 上 分配 X
            X* q = new(gpShared) X(i);     // 在 共享内存 上 分配 X
            // ...
        }
        
        // 销毁: 显式调用 dtor
        void destory(X* pX, B* pB)
        {
            pX->~X();      // 显式调用 dtor: 清理资源
            pB->free(pX); // 释放内存
        }
    

    11.3 列表 {}

            {} 列表
                |
                |   及其 底层数组 `lifetime` 与 initializer_list 对象 相同
                |/
        编译器 构建 临时数组 作 `底层数组`
                        |   
                        |   不可修改
                        |/
        构建 `initializer_list 对象` 管理之
            
            小对象: 以 值传递 可行
    

    11.4 lambda 表达式

    
    (1) `回调`
        1) 操作 作 实参 传给 算法
        
        2) 再通过 function pointer / function object / lambda 
            
            去调 函数调用运算符 operator() (paraList)
            
            function pointer 本身不能 持有 state, 只能由 算法 提供
    
    (2) mutable 修饰符
    
        默认下, operator() 是 const: const 含义
    
        lambda body 内 
            不修改 通过 `值捕获的变量 的 copy`
            note:而不是 不修改 通过引用捕获的变量的状态           
    
    (3) vs. 函数      
        
        函数 无 捕获功能
        
        => lambda 可作 局部函数, 普通函数不能
        
    1 实现
    
        函数对象
    
        void f(const vector<int>& v, ostream& os, int n)
        {
            for_each(v.begin(), v.end(), 
                     [&os, n](int x) { if(x % n == 0) os << x << '\n';  } );
        }               |         | 
                        |         
        class F         |         |
        {              /|           
            ostream& os;|         |
            int n;      |         
        public:        /          |
            F(ostream& s, int nn):  os(s), n(nn) {}
                                  /
            void operator() (int x) const
            {
                 if(x % n == 0) os << x << '\n';  }
            }
        };
        void f(const vector<int>& v, ostream& os, int n)
        {
            for_each(v.begin(), v.end(), F{os, n} );
        }
        
    2 捕获
    
        (1) lambda 主要用途
            
            `封装` 部分代码 以便将其用 `作 参数`
            
            定义处使用
                而不用像 函数/函数对象 那样, 分离定义与使用
        
        (2) 3种捕获形式
        
            1) 隐式捕获
            
                [=] 引用传递
                [&] 值传递
                
            2) 显式捕获
            
                [捕获列表]
                    可捕获 this
                    
            3) 混合 隐式 与 显式 捕获
                
                捕获列表中 `没出现的 名字(外层 context 局部变量)`
                    通过 引用 / 值 传递, 列出的 通过 值/引用
                         /       |
                        /       [=, 捕获列表]
                       /                  | 
                    [&, 捕获列表]       必须 以 & 为 前缀
                            |           => 不可出现 this
                            |/
                        不能 以 & 为 前缀
                        => 可出现 this
                        
        (3) 很多时候, 既可 捕获, 也可 传实参
                
        (4) lambda 与 this
        
            1) 捕获 this: [this]
                            
                通过 this 访问 `类对象的 成员`
            
            2) [this] 与 [=] 不兼容
            
                => 多线程 中易发生 `竞争条件`
            
            
            class A
            {
                int value;
                int result;
            public:
                void execute()
                {
                    [this]() { result = f(value); }
                }
                
                int f(int v);
            };
    

    11.5 显式 类型转换 / 强转 casting

    1 列表 T {v}

        (1) 用 值 v 构造 T 类型的临时对象
    
        (2) 好处
            
            只执行 "行为良好的"  类型转换
                    
            存在
            [1] v 向 T 的 `非窄化` 类型转换, 或
        
            [2] 正确的 `T 类型  ctor`
                T{} 
                    构造函数: 类型 T  默认值
    

    2 4种 命名转换

        const_cast
            为某些 const 对象 获得 写入权利
            
            仅对 const / volatile 
            
        reinterpret_cast
            改变 位模式 的 含义
                
            `非关联类型 间` 转换
                
                1> 整数 -> 指针
                
                2> 指针 -> 非关联指针
            
                IO_device* p = reinterpret_cast<IO_device*>(0xFF00); // 0xFF00 处的 设备
                
        static_cast
        
            `关联类型 间` 转换
                
                `原始内存` 指针转换
                
                    int* p = static_cast<int*>(my_allocator(100) );
                
                1> 整数 -> 枚举 / 浮点 -> 整数
                
                2> 指针 -> 同一类层次中 其他指针
                
                3> (单参数) explicit ctor
                        |
                        |   
                        |/
                    X::X(T v)
                    
                4> `类型转换运算符`
                        |
                        |   X(类类型) 向 T(任意类型) 的 类型转换
                        |/
                    X::operator T() 
    
        dynamic_cast
            `动态检查` 类层次 关系
        
            执行 `运行时检查` // static_cast 不做 运行时检查
    

    3 C 风格 转换

        (T)v
          |
          |/
        可执行 命名转换 中 除 dynamic_cast 之外的 3种转换的 任意组合
    

    4 函数形式 的转换

        T(v)
         |
         |  对 内置类型 T <=>
         |/
        (T) V       
    

    第12章 函数

    12.1 函数声明

        1 返回 void 
            
            并不存在 void 值, 但可 `调用 void 函数` 作 `另一 void 函数 的 返回值`
                
                void g(int*p);
                
                void h(int* p)
                {
                    // ...
                    return g(p); // <=> g(p); return;
                }
            
        2 inline 函数
            
            遵循 ODR(一次定义法则) 
            
                定义出现在 多个编译单元 (通常是因为 inline 放 头文件)
                    定义必须保持一致
    
        3 constexpr 函数
            
            遵循 ODR(一次定义法则) 
            
                可视为 更严格的 inline
            
        4 local static 对象
    
            在 `函数多次调用` 间 `维护1份公共信息`, 
    
                而无需使用 `global 变量` -> 可能被 其他不相关含访问   
    

    12.2 传参

    1 引用传递

        与 引用初始化 规则相同
        
            `字面值 / 常量 / 需类型转换的参数` 可传给 `const 引用` (const t&)
                                |
                                |/
                    转换后的值 存入 临时变量 
                                |
                                |   传给
                                |/
                            const 引用
                
            float fsqrt(const float&);                  
            void g(double d)
            {
                float r = fsqrt(d); // 传递 存 static_cast<float>(d) 的 临时变量
            }
    

    2 传 数组的引用: paraType 数组引用 类型

        函数模板 实参推断: size 也是 paraType 的一部分 
        
        template<typename T, int N>
        void f( T(&r)[N] );
    

    12.3 重载函数

    1 自动 重载解析

        (1) 概念
            
            调用函数 f 时, 由 compiler 据 `实参类型` 
                与 `scope 中` 名为 f 的 各函数 作 `最佳匹配`
        
        (2) 匹配顺序
             
            1) 精确 匹配
            
                [1] 无须 类型转换
                
                [2] 仅需 简单类型转换
                    
                    数组名 -> 指针
                    函数名 -> 函数指针
                    T      -> const T
            
            2) 提升 后匹配
                
                [1] bool/char/short -> int
                             
                    ... unsigned 版本 
                                
                [2] float -> double 
            
            3) 标准类型转换 后匹配
            
                [1] 算术类型 间 `强转`
                    
                    整型间、整型与浮点型间 
                
                [2] OO/GP 中 指针 `隐转`
                
                    类层次 中 
                        Derived* -> Base*
                
                    模板中
                        T* -> void*
                        
            4) 用户自定义类型转换 后匹配
            
                [1] 单参数 explicit ctor
                
                [2] 类型转换运算符 X::operator T() 
            
            5) 使用 函数声明中 省略号 ...匹配
        
            二义性 => compile error 
                    能找到的 最高层级(同一层) 中 不止1个匹配 
                    
        (3) 函数模板 的 special
            
            模板实参推断 -> 特例化版本 -> 参与 重载解析规则
            
            23.4.2 节
    
            ——————————————————————————————————————————————————————————————————————————————————————————————
            模板 函数           |  (先) `引用推断` (模板实参推断) + (再决定) 右值引用传递 / (还是) 左值引用传递 
                                |           |
                右值引用模板实参|           | 区分 左值 和 右值
                                |           |/
                                |   X 类型 的 `左值 / 右值` 被 `推断为 X& / X`
                                |       
            ———————————————————————————————————————————————————————————————————————————————————————————————             
            非模板 函数      |  (右) 值 绑定到 右值引用
                                |               |
                右值引用实参  |             移动 
            ——————|—————————————————————————————————————————————————————————————————————————————————————————    
                  |
                  |/                      
            void f(vector<int>&& );         
        
        (4) 模板函数 重载: 23.4.3 节
            
            ——————————————————————————————————————————————————————
                5句话
            ——————————————————————————————————————————————————————
            1) 找 `所有 特例化版本`
            ——————————————————————————————————————————————————————
            2) 找 `更特殊的`
            ——————————————————————————————————————————————————————
            3) 与 普通函数 一起, 按 `普通函数 重载解析` 规则
            ——————————————————————————————————————————————————————
            4) 普通函数 与 特例化版本 匹配得一样好, `优选 普通函数`
            ——————————————————————————————————————————————————————
            5) `无法确定 最佳匹配` => 二义性 => 编译报错
            ————————————————————————————|——————————————————————————
                                        |
            二义性消除
                template<typename T>
                T max(T, T);
                
                max('a', 1); // max<char, char> 还是 max<int>(int)
                max(2.7, 4); // max<double, double> 还是 max<int>(int)
                    
                1> 显式限定
                    
                    max<int>('a', 1);    // max<int>(int)
                    max<double>(2.7, 4); // max<double>(double)
                    
                2> 封装 1层
        
            1) 找参与重~析的 `所有 特例化版本`
                
                调 sqrt(z)
                    sqrt<double>(complex<double>) 与
                    sqrt<complex<double>>(complex<double>) 都成为 候选
                    
            2) 若 2个 模板函数 都可调用, 找`更特殊的`
                    
                    调 sqrt(z)
                        sqrt<double>(complex<double>) 比
                                |
                                |
                                T
                        sqrt<T>(complex<T>)
                        
                            sqrt< complex<double> >(complex<double>)
                                    |
                                    |
                                    T
                            sqrt<T>(T)
                            
                        更特殊
                    
                    因为, 任何匹配 sqrt<T>(complex<T>) 的 调用, 也都能匹配 sqrt<T>(T)
                        
            3) 还留在 `候选集` 中的 
                          |
                          |
                `模板函数 和 普通函数`
                          |
                          | 用
                          |/
                `普通函数 重载解析规则`           
                    
                    sqrt(2)
                        sqrt<int>(int) 优于 sqrt(double)
    
                note:
                    参与了 `模板实参推断` 的 `(模板) 函数实参`,                 
                        不能再进行 提升、标准类型转换、用户自定义类型转换
            
            4) 若 普通函数 与 特例化版本 `匹配的一样好`
                
                优选 `普通函数`
                
                    sqrt<2.0>
                        sqrt(double) 优于 sqrt<double>(double)
            
            5) 多个一样好的匹配, 且 
            
                `无法确定 最佳匹配` => 二义性 => 编译报错
    

    2 重载 与 returnType

        重载解析过程 `不考虑 returnType`
    

    3 重载 scope

        —————————————————————————————————————————————————————————————————       
                1) 显而易见 的 scope: context scope / 函数调用前的 global scope
        显式
                2) using 指示 / using 声明
                    引入的 跨 类 作用域
        ————————————————————————————————————————————————————————————————        
                3) 重载函数 被 类成员调用
                    引入 类 及其 基类 scope
        隐式 
                4) ADL 引入的 scope / 关联 namespace
                
                    ——————————————————————————————————————————————————————— 
                                        |   ADL 的 关联名字空间             
                                        |   ( associated namespace ) ADL scope
                    ———————————————————————————————————————————————————————
                    函数调用 f(arg...)  |
                    的参数 args... 是   |
                        ———————————————————————————————————————————————————
                        内置类型        |       无 
                        ———————————————————————————————————————————————————
                        namespace 成员    |   namespace 
                        ———————————————————————————————————————————————————
                        class 成员        |   类 + 其基类 + 类所在的 namespace
        ———————————————————————————————————————————————————————————————————             
    

    4 函数 重载 优先级 (14.2.4 节)

        ———————————————————————————————————————————————————————————————————
                                    函数 重载 优先级
        函数调用 f(arg...) 在 
        ———————————————————————————————————————————————————————————————————
        (1) 非类成员调用 
            
                                    [1] 函数调用 所在 scope (global scope) 
            
                                    [2] ADL scope
        ——————————————————————————————————————————————————————————————————— 
        (2) 类成员调用 
            ———————————————————————————————————————————————————————————————
            1) 命名函数调用 
                
                                    [1] 优先 类 scope + 基类 scope 
                
                                    [2] 再   ADL scope 
            ———————————————————————————————————————————————————————————————
            2) 匿名函数调用(运算符)
                
                                    [1] 类 scope + 基类 scope + ADL scope 
        ———————————————————————————————————————————————————————————————————                                                          
    
        // === (1)
        1) 命名函数调用 f(x) + 在 类 D 成员 g() 内调用, `实参 x` 类型 N::S 
        
            find scope 
                D+Base 的 scope
                x 的 ADL scope 
                global 
                
            find order 
                [1] 先 D+Base 的 scope 中 找, 找到 Base::f(N::S) => 匹配调用 
        
        2) 实参1
            find order  
                [1] 先 D+Base 的 scope 中 找, 没找到 
                [2] ADL 无法实现 => 查不到 h(int) => 编译报错
        
        namespace N
        {
            struct S {int i};
            
            void f(S);
            void h(int);
        };
        
        struct Base
        {
            void f(N::S); 
        };                                  
                                            
        struct D: Base                      
        {                               
                
            void g(N::S x)  
            {                   
                f(x);    // 1)
                h(1);    // 2) 
            }
        };
        
        // === (2) 匿名函数调用
        匿名函数调用 operator!(X) 在 类 Z 成员 f() 内调用, `实参 x` 类型 X  
        
            find scope: 
                Z 的 scope & global scope 
            
            find order: 2 个 scope 同优先级 
                
                Z::operator!() 不匹配 
                
                ::operator!(X) 匹配 => 调用 
                
        X operator!(X);
    
        struct Z
        {                                                       
            Z operator!();             
                                         
                                         
            X f(X x)            
            {                               
                // ...                          
                return !x;  //(1)
            }           
                            
            int f(int x) 
            { return !x; } //(2) 对 int 调 内置 !
        };
        
        // === 3 命名函数调用 f(x, 1) + 非类成员调用 
        find scope: 
            global scope 
            参数 x 的 ADL scope: N::X 是 namespace N 的 成员 => ADL scope = N 
        
        find order:
            2 个 scope 同优先级 
            
            N::f(X, int) 比 N2::f(N::X, unsigned) 更匹配
            
            => 调 N::f(X, int) 
            
        namespace N
        {
            template <class T>
                void f(T, int);
            class X {};
        };
        
        namespace N2
        {
            N::X x;
            
            void f(N::X, unsigned);
            
            void g()
            {     
                f(x, 1); // 调 N::f(X, int)
            }
        };
    

    12.4 函数指针

    1. C <-> C++ 函数指针 的 赋值/初始化

        type `精确匹配`
                
        (1) 实参 与 形参 `函数指针 类型` 必须 `精确匹配`
                    
            C 编译
                using CFT = int(const void*, const void*);
                void ssort(void* base, size_t n, size_t sz, CFT cmp);
            
            C++ 编译
                int cmp(const User* p, const User* q) // User: 用户自定义类型 struct
                {
                    return strcmp(p->id, q->id);
                }
                
            cmp 作 实参 调 ssort => 两种 函数指针 类型 非 精确匹配 => 编译报错
            
            解决
                中间层 
                        const void* p 
                            |
                            |/
                        static_cast<const User*>(p)
                        
                    int cmp(const void* p, const void* q)
                    {
                        return strcmp(static_cast<const User*>(p)->id, static_cast<const User*>(q)->id);
                    }
                    
                    |   C与C++ 链接方式/链接方式 (=>调用机制) 不同 => 
                    |/
                仍报错
                    
        (2) C & C++ code 链接方式/链接规范 要相同 (=>调用机制 相同)
                                
            // file1.c 
            extern "C"
            {
                using CFT = int(const void*, const void*);
                void ssort(void* base, size_t n, size_t sz, CFT cmp);
            }
            
            // file2.cpp
            extern "C" int cmp(const void* p, const void* q);
                        
    

    2 作 操作参数 调 算法/函数

    回调
    

    3 成员 函数指针 与 普通函数指针 本质不同

            |
            |/
         是 vtbl 的 index: 0 / 1 / 2 / ...
    

    12.5 宏

    编译器 接触 程序前, 宏 会 `重排程序文本`
        => 调试器、交叉引用、性能评测工具 很难发挥作用
    
        (1) 宏名 不允许 `重载`
            
            宏 无法处理 `递归`
            
        (2) 尽管尽量用 括号() 将 `宏的参数 括起来`
        
            只能解决 简单语法问题,
                |
                | 但对于
                |/
            `宏产生的 副作用` 仍 `无能为力`
            
            #define MIN(a, b) ( ( (a) < (b) ) ? (a) : (b) )
                              |                            |
                              |/                           |/   
                             最外层 也加 括号           不是语句 => 末尾没 分号
                             
            int x = 1;
            int y = 10;
            int z = MIN(x++, y++); // x 变成 3, y 变成 11
            
                                      ? 会使 x/y 均 递增 => 选中 (x++) => x 再递增
                                        |
                                        |
                    ( ( (x++) < (y++) ) ? (x++) : (y++) )
                        |
                        | (x++) < (y++) 是表达式 
                        |/
                      其值为 true
    
        (3) 宏 的应用
        
            1) `字符串 拼接`
            
                ## 
                
                #define NAME2(a, b) a##b
                int NAME2(hack, cah)();
                
                展开为
                int hackcah();
        
            2) 宏 的 `空 参数列表`
        
                #define EMPTY() std::cout << "empty\n"
                
                EMPTY(); // 输出 "empty\n"
        
            3) 变长参数 宏
            
                #define err_print(...) fprintf(stderr, "error: %s %d\n", __VA_ARGS__)
                                                                            |
                                                                            |/
                                                                         把 实参 当作 字符串
                err_print("The answer", 54);
                
                // 输出: The answer 54
                
    1 条件编译
    
        有一种 宏 无法替代
        
            条件编译
            
                #ifdef 等
    
    2 预定义宏
    
        __cplusplus 
            C++ 编译器中 定义, C 编译器 没有
                C++11 中其 值为 201103L
                
        __LINE__
            当前源文件 的 代码行数
    
    3 编译指令
    

    相关文章

      网友评论

          本文标题:11-12 逻辑 位 递增 / 自由存储 / lambda /

          本文链接:https://www.haomeiwen.com/subject/eyyevrtx.html