类型
nullptr 和std::nullptr_t
- C++11引入了nullptr取代0和NULL,表示指向一个不确定的值。这样可以避免“null pointer被解释为一个整数值”时的错误
void f(int);
void f(void *);
f(0);
f(NULL);
f(nullptr);
- nullptr是个新关键字,它被自动转换为各种pointer类型,但是不会被转换为任何整型,它是一个std::nullptr_t的类型
除此之外,它引入了一些新类型:
- char16_t、char32_t
- long long 、unsigned long long
auto:自动推导变量类型
- auto根据变量的初值自动推导其类型。因此一定要有初始化操作
auto i = 11;
auto j;
decltype:推导表达式类型
decltype 自动推导编译器类型。这其实是typeof 的特性体现。只不过原来的typeof 缺乏一致性又不安全,所以引入了decltype
std::map<std::string, float> coll;
delctype(coll)::value_type elem;
应用场景:
举例:
有时候,有一种需求,函数的返回类型取决于某个表达式对实参的处理。在C++11之前是没法处理的,因为返回式所使用的对象尚未被引入。而C++11其,可以将一个函数的返回类型转而声明于参数列之后:
template<typename T1, typename T2>
auto add(T1 x, T2 y) ->decltype(x + y);
noexcept:声明不会抛出异常,如果内部有异常那么程序终止
- noexcept:
- 说明某个函数不打算或者说无法抛出异常。
- 比如下面声明了f()不打算抛出异常。如果有异常未在foo()中处理,那么程序将会终止。然后
std::terminate() 被调用并默认调用std::abort()
void f()noexcept;
-
为什么要引入noexcept:
- 运行期校验:C++只检验运行期异常而不能检验编译器异常,所以它无法保证每个异常都被处理。
- 运行期开销:运行期检验会令编译器产生额外代码并妨碍优化
- 无法用于泛型码中:泛型码往往无法知道哪一类异常可能被“操作template实参”的函数抛出,所以我们无法写出正确的异常明显
- 函数大致分为两种:
noexcept 刚好对应这两种情况
constexpr:常量表达式
-
作用:
- 常量表达式指的是在编译过程中就能确定计算结果的表达式
-
为什么要引入常量表达式:
- 类型1+2, 3*4这种表达式总是会产生相同的结果并且没有任何副作用。如果编译器能够在编译时就把这些表达式直接优化并植入程序运行时,将能增加程序的性能
- 计算的时机从运行期提前到编译期,比宏定义效率更高
constexpr int sq(int i){
return i * i;
}
float a[sq(9)];
foreach循环
for(decl : coll){
stactememt
}
lambda表达式
强类型枚举
之前的枚举
enum {
General,Light,Medium,Heavy
};
enum Type{
General,Light,Medium,Heavy
};
C++11之后引入的枚举
enum class Type:int{
Generay,
Light,
Medium,
Heavy
};
enum class Type:char{
Generay,
Light,
Medium,
Heavy
};
重点在于,在enum之后的关键字class 。
优点:
初始化相关
一致性初始化
- C++11之前, 初始化方式,乱七八糟,不同编译器的对待方式也不太一样。为此,C++引入了所谓的一致性初始化语法,也就是
{} 来一统江湖
int values[]{ 1,2,3,4,5,6,7,8,9 };
int* a = new int[3] { 1, 2, 0 };
std::vector<char> vec{'a','b' ,'c' ,'d' ,'e' };
std::map<int, std::string>m1{ { 1,"a" },{ 2,"b" },{ 3,"c" },{ 4,"d" } };
- 为了让用户自定义类型也可以使用
{} ,C++11引入了class template std::initializer_list<>
class MaginFoo{
public:
MaginFoo(std::initializer_list<int> list){
....
}
};
MaginFoo maginFoo = {1, 2, 3, 4};
右值
右值可以说是C++11中最重要的特性了。我们必须先了解下什么是左值,什么是右值。简单来说:
auto right = std::move(left);
为什么要引入右值呢?是为了实现移动语义和完美转发
什么是移动语义呢?
- 移动就是搬移,简单理解将左值所拥有的资源抢过来,然后左值就可以销毁了。
- 在C++11之前,如果想用其他对象初始化一个同类的新对象,只能借助类的拷贝构造函数和拷贝赋值函数,必须先将新对象复制一份和其他对象一模一样的数据,然后就把老的值销毁
class X{
public:
X(const X&);
X& operator=(const X&);
};
- 而引入右值之后,就有了移动构造函数和移动赋值函数,它可以将原来的值抢到新的值之上。这就少了一份拷贝操作,效率会快很多
class X{
public:
X(const X&&);
X& operator=(const X&&);
};
关于完美转发
模板相关
不定参数模板
- 采用递归的方式拆解函数模板中的不定参。一定要有一个特化模板去做终止条件
- 其中
sizeof...(args) 会生成实参个数
void print()
{
cout<<"The end!!!"<<endl;
}
template<typename T,typename ... Types>
void print(const T &fristArg,const Types&... args)
{
cout<<fristArg<<endl;
print(args...);
}
int main(int argc, char const *argv[])
{
print("hhaah",112,333,222,999,000);
return 0;
}
C++11新特性之不定参模板
alias template
智能指针
|