mutable
是为了突破 const 的限制而设置的。可以用来修饰一个类的成员变量。被 mutable 修饰的变量,将永远处于可变的状态,即使是 const 函数中也可以改变这个变量的值。
explicit
跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式)
explicit可以防止隐式转换的发生,explicit关键字只对有一个参数的类构造函数有效, 如果类构造函数参数大于或等于两个时, 是不会产生隐式转换的, 所以explicit关键字也就无效了
例如:一个类A的构造函数A(int i)就是,既可以用来作为构造器,又可以实现隐式转换A a=1;因为1可以通过构造函数A(int i)转换为一个类A的对象。(隐含的类型转换操作符)
但有时我们并不想让他进行隐式类型转换,这时C++的explicit关键字就起到作用了.
注意:当类的声明和定义分别在两个文件中时,explicit只能写在在声明中,不能写在定义中。
volatile
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统,硬件或者其他线程等。
遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。声明时语法:int volatile vInt; 当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据(而不是寄存器),即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存
final
final用于修饰类,该类不能再被继承
inal用于修饰虚函数,表明子类不能重写该虚函数,为”终结虚函数“。
override
假如我们继承基类的虚函数,在重写虚函数时写错了,参数类型不对或个数不对,但是编译没问题,造成了对基类同名函数的隐藏,运行时候和设计的不一样,
override就是辅助检查是否正真重写了继承的虚函数,例如:
struct B4{
virtual void g(int) {}
};
struct D4 : B4{
virtual void g(int) override {} // OK
virtual void g(double) override {} // Error
};
default
我们知道,C++98和C++03编译器在类中会隐式地产生四个函数:默认构造函数、拷贝构造函数、析构函数和赋值运算符函数,它们被称为特殊成员函数。
在 C++11 中,被称为 “特殊成员函数” 的还有两个:移动构造函数 和 移动赋值运算符函数。如果用户申明了上面六种函数,编译器则不会隐式产生。
C++引入的default关键字,可显示地、强制地要求编译器为我们生成默认版本
class DataOnly{
public:
DataOnly()=default; //default constructor
~DataOnly()=default; //destructor
DataOnly(const DataOnly& rhs)=default; //copy constructor
DataOnly& operator=(const DataOnly & rhs)=default; //copy assignment operator
DataOnly(const DataOnly && rhs)=default; //C++11,move constructor
DataOnly& operator=(DataOnly && rhs)=default; //C++11,move assignment operator
};
上面的代码,就可以让编译器生成上面六个函数的默认版本。
delete
1、禁止编译器生成上面六种函数的默认版本。
class DataOnly{
public:
DataOnly()=delete; //default constructor
~DataOnly()=delete; //destructor
DataOnly(const DataOnly& rhs)=delete; //copy constructor
DataOnly& operator=(const DataOnly & rhs)=delete; //copy assignment operator
DataOnly(const DataOnly && rhs)=delete; //C++11,move constructor
DataOnly& operator=(DataOnly && rhs)=delete; //C++11,move assignment operator
};
2、C++11 中,delete 关键字可用于任何函数,不仅仅局限于类成员函数。在函数重载中,可用delete来滤掉一些函数的形参类型
bool isLucky(int number); // original function
bool isLucky(char) = delete; // reject chars
bool isLucky(bool) = delete; // reject bools
bool isLucky(double) = delete; // reject doubles and floats
3、在模板特例化中,也可以用 delete 来过滤一些特定的形参类型。
例如,Widget 类中声明了一个函数模板,当进行模板特化时,要求禁止参数为 void* 的函数调用。
class Widget{
public:
template<typename T> void processPointer(T* ptr){}
};
template<> void Widget::processPointer<void>(void*)=delete; //deleted function template
thread_local
....
decltype
decltype 关键字是为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的。它的用法和 sizeof 很相似:
decltype(表达式)
在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
有时候,我们可能需要计算某个表达式的类型,例如:
auto x = 1;
auto y = 2;
decltype(x+y) z;
拖尾返回类型、auto 与 decltype 配合
你可能会思考,auto 能不能用于推导函数的返回类型。考虑这样一个例子加法函数的例子,在传统 C++ 中我们必须这么写:
template<typename R, typename T, typename U>
R add(T x, U y) {
return x+y
}
这样的代码其实变得很丑陋,因为程序员在使用这个模板函数的时候,必须明确指出返回类型。但事实上我们并不知道 add() 这个函数会做什么样的操作,获得一个什么样的返回类型。
在 C++11 中这个问题得到解决。虽然你可能马上回反应出来使用 decltype 推导 x+y 的类型,写出这样的代码:
decltype(x+y) add(T x, U y);
但事实上这样的写法并不能通过编译。这是因为在编译器读到 decltype(x+y) 时,x 和 y 尚未被定义。为了解决这个问题,C++11 还引入了一个叫做拖尾返回类型(trailing return type),利用 auto 关键字将返回类型后置:
template<typename T, typename U>
auto add(T x, U y) -> decltype(x+y) {
return x+y;
}
从 C++14 开始是可以直接让普通函数具备返回值推导,因此下面的写法变得合法:
template<typename T, typename U>
auto add(T x, U y) {
return x+y;
}