空指针 nullptr
nullptr出现的目的是为了替代NULL
在某种意义上来说,传统 C++ 会把 NULL、 0 视为同⼀种东⻄,这取决于编译器如何定义 NULL,有些编译器会将NULL 定 义为((void*)0)
,有些则会直接将其定义为 0。C++ 不允许直接将void *
隐式转 换到其他类型,但如果NULL 被定义为((void*)0)
,那么当编译 char *ch = NULL; 时,NULL 只好被定义为 0。⽽这依然会产⽣问题,将导致了 C++ 中重载特性 会发⽣混乱,考虑:
void func(int);
void func(char *);
对于这两个函数来说,如果 NULL ⼜被定义为了 0 那么 func(NULL) 这个语句将 会去调⽤ func(int),从⽽导致代码违反直观。
为了解决这个问题,C++11 引⼊了 nullptr 关键字,专⻔⽤来区分空指针、0。nullptr 的类型为nullptr_t,能够隐式 的转换为任何指针或成员指针的类型,也能和他们进⾏相等或者不等的⽐较。
当需要使⽤ NULL 时候,养成直接使⽤ nullptr 的习惯。
右值引⽤
C++03以及之前的标准中,右值是不允许改变的,实践中也通常使用const T&
的方式传递右值。然而这是效率低下的做法,比如:
Person get(){
Person p;
return p;
}
Person p = get();
上述获取右值并初始化 p 的过程包含了 Person 的3个构造过程和2个析构过程。 这是 C++ ⼴受诟病的⼀点,但是C++11的右值引用特性允许我们对右值进行修改。借此可以实现move语义,即从右值中直接拿数据过来初始化或者修改左值,而不需要重新构造左值后再析构右值。一个move构造函数是这样声明的:
class Person{
public:
Person(Person&& rhs){...}
...
}
常量表达式
对于如下:
int N = 5;
int arr[N];
编译器会报错: error: variable length array declaration not allowed at file scope int arr[N];
,但 N 就是5,不过编译器不知道这⼀点,于是我们需要声明为 const int N = 5 才可以。但C++11的泛化常数给出了解决⽅案:
constexpr int N = 5; // N 变成了⼀个只读的值
int arr[N]; // OK
constexpr 告诉编译器这是⼀个编译期常量,甚⾄可以把⼀个函数声明为编译期常量表达式。
constexpr int getFive(){ return 5; }
int arr[getFive() + 1];
final 和 override
C++借由虚函数实现运行时多态,但C++的虚函数又有很多脆弱的地方:
- ⽆法禁⽌⼦类重写它。可能到某⼀层级时,我们不希望⼦类继续来重写当前虚函数了。
- 容易不⼩⼼隐藏⽗类的虚函数。⽐如在重写时,不⼩⼼声明了⼀个签名不⼀致但有同样名称的新函数。
C++11提供了final来禁止虚函数被重写/禁止类被继承,override来显式的重写虚函数。这样编译器给我们不小心的行为提供更多有用的错误和警告
struct Base1 final { };
struct Derived1 : Base1 {}; // 编译错:Base1不允许被继承
struct Base2 {
virtual void f1() final;
virtual void f2();
};
struct Derived2 : Base2 {
virtual void f1(); // 编译错:f1不允许᯿写
virtual void f2(int) override; // 编译错:⽗类中没有 void f2(int)
};
default 和 delete
我们知道编译器会为类自动生成一些方法,比如构造和析构函数。
现在我们可以显示的指定和禁止这些自动行为了
struct classA {
classA() = defauult; // 声明⼀个⾃动⽣成的函数
classA(T value);
void *operator new(size_t) = delete; // 禁⽌⽣成new运算符
};
在上述 classA 中定义了 classA(T value) 构造函数,因此编译器不会默认⽣成⼀个⽆参数的构造函数了, 如果我们需要可以⼿动声明,或者直接 = default 。
静态断言
C++提供了两种方式来assert:
- 一种是assert宏
- 一种是预处理指令#error
前者在运行期起作用,后者在预处理器起作用。
它们对模板都不好使,因为模板是是编译器概念。为此引入了static_assert
template< class T >
struct Check {
static_assert( sizeof(int) <= sizeof(T), "T is not big enough!" ) ;
} ;