Skip to content

第二章-保证稳定和兼容性

回到目录

2.1 保持与C99兼容

  • 预定义宏

  • __func__预定义标识符

    • __func__可以显示当前的函数名字
    • 还可以放在类或者结构体中
      #include <iostream>
      
      class TestClass {
      public:
          TestClass () : name (__func__) {}
          const char *name;
      
          // failed 由于在参数声明时,__func__还未被定义
          // TestClass () {}
          // const char *name = __func__;
      };
      
      int main (int argc, char *argv[]) {
          TestClass tc;
          std::cout << tc.name << std::endl;
          return 0;
      }
      
      
      // out
      // TestClass
      
  • _Pragma操作符

    • #pragma once 与头文件的防止重复包含的功能一样,在C++11中是保留了 _Pragma,所以在C++11中, #pragma once = _Pragma("once")
  • 变长参数的宏定义以及__VA_ARGS__

在C99标准中,可以这样使用#define PR(...) printf(__VA_ARGS__),例如一个简单的LOG例子

#include <iostream>

#define LOG(...) { \
    fprintf(stderr, "%s: line %d: \t", __FILE__, __LINE__); \
    fprintf(stderr, __VA_ARGS__); \
    fprintf(stderr, "\n"); \
}

int main (int argc, char *argv[]) {
    int a = 7;
    LOG("a = %d", a);
    return 0;
}

// out
// test.cpp: line 11:      a = 7

2.2 long long整型

long long整型有两种:long longunsigned long long。在C++11中,标准要求long long整型可以在不同平台上有不同的长度,但至少有64位。

2.3 扩展的类型

比如UINT__int16u64int64_t,等等。这些类型有的源自编译器的自行扩展,有的则是来自某些编程环境(比如工作在Linux内核代码中),不一而足。而事实上,在C++11中一共只定义了以下5种标准的有符号整型: signed char, short int, int, long int, long long int

2.4 宏__cplusplus

常见的就是在c与c++混合编写的代码中,经常用这个宏定义。 第二个就是可以判断编译器是否支持c++11#if __cplusplus < 201103L ......

2.5 静态断言

在通常情况下,断言就是将一个返回值总是需要为真的判别式放在语句中,用于排除在设计的逻辑上不应该产生的情况。比如一个函数总需要输入在一定的范围内的参数,那么程序员就可以对该参数使用断言,以迫使在该参数发生异常的时候程序退出,从而避免程序陷入逻辑的混乱。

  • 运行时与预处理时的断言

在C++中,标准在<cassert><assert.h>头文件中为程序员提供了assert宏,用于在运行时进行断言。

在C++中,程序员也可以定义宏NDEBUG来禁用assert宏。这对发布程序来说还是必要的

#error,下面的代码,预处理时就会报错,可以提示一些信息

#ifndef _HRAD_H_
#error "asdf."
#endif

  • 静态断言static_assert

    编译时报错

    static_assert(常量表达式,提示字符串)

    eg, static_assert(1 == 2, "a must equire b");.编译报错:test.cpp:11:5: error: static assertion failed: a must equire b

2.6 noexcept

针对异常的关键字

2.7 快速初始化成员变量

  • 类内部初始化变量,就地初始化,
  • 初始化列表的效果总是优先于就地初始化的,也就是说 {} > = or ()

2.8 非静态成员的sizeof

class A {
public:
    int a;
};

int main (int argc, char *argv[]) {
    // c++98编译不过,c++11编译通过
    std::cout << sizeof(A::a) << std::endl;         // result is 4
    // c++98,可以这样获得非静态成员的大小
    std::cout << sizeof(((A *)0)->a) << std::endl;  // result is 4

    return 0;
}

2.9 扩展的friend语法

友元可以无视类中成员的属性。无论成员是public、protected或是private的,友元类或友元函数都可以访问,这就完全破坏了面向对象编程中封装性的概念。因此,使用friend关键字充满了争议性

  • 使用上
class A {
    friend class Poly;  // C++98 pass, C++11 pass
    friend Poly;        // C++98 fail, C++11 pass
};
  • 关于模板的友元

TODO

2.10 final 和 override

  • final关键字的作用是使派生类不可覆盖它所修饰的虚函数。
  • 引入了虚函数描述符override,如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,否则代码将无法通过编译。

  • 总结 关于 static const final override 默认参数 的在类内外的使用

    class Base {
    public:
        virtual void final_test() = 0;
        virtual void override_test() = 0;
    };
    
    class ImpBase : public Base{
    public:
        // static放在声明中
        static void static_test();
        // const声明和实现都需要加上
        const void const_test() const;
        // final和override在函数声明中加
        virtual void final_test() final;
        // final和override在函数声明中加
        virtual void override_test() override;
        // 默认参数在声明和实现只能存在一处,如果两处都有,那么两个默认参数到底是哪个呢?
        void default_test(int a = 10);
    
        static int a;
    };
    
    // 类外初始化静态成员
    int ImpBase::a = 50;
    
    void ImpBase::static_test() {
        std::cout << "static_test" << std::endl;
    }
    const void ImpBase::const_test() const {
        std::cout << "const_test" << std::endl;
    }
    void ImpBase::final_test() {
        std::cout << "final_test" << std::endl;
    }
    void ImpBase::override_test() {
        std::cout << "override_test" << std::endl;
    }
    void ImpBase::default_test(int a) {
        std::cout << "default_test" << std::endl;
    }
    

2.11 模板函数的默认模板参数

TODO

2.12 外部模板

TODO