说明
右值引用(Rvalue reference,T&&)解决了了左值引用(T&)无法传递临时对象和常引用
(const T&)传递的对象只读的问题。右值引用允许传递一个可变更的临时对象引用,主要用于配合移动构造。 移动构造(move
constructor)使用移动而非复制语义完成构造过程,主要用于解决从函数返回值时的大量深拷贝开销。使用右值引用和移动构造,在按值传递和返回临时对象时,即可免去不必要的内存分配和数据拷贝开销。
|
优点
- 在特定情况下,可实现内存零拷贝,从而大大提高执行效率。
|
缺点
-
进一步复杂化了对象传递的语义。可想而知,复合使用了传址(指针、引用、右值引用,和它们的各种常量变体)以及传值等各种对象传递方法的代码,其学习和维护成本都将较高。并且无论对其使用者还是实现者来说,都比较容易产生
bug。
- 需要为每个对象考虑多实现一种新的构造方法。
- 在传统的 C++03
代码中,通过智能指针等手段完全可以实现同样的零拷贝。若使用了带有引用计数/写时拷贝等特性的代理实现,还能够达到更好的零拷贝效果和更优的性能。
- 通过智能指针等手段达到零拷贝的目的,其方便性和易用性至少能够与右值引用+移动构造相当。
|
总结
只有在实现标准库等某些必须返回值(而不能返回智能指针等句柄对象,也不能返回引用)的场合,才考虑使用右值引用+移动构造方案。其它情况下,智能指针或代理类通常是更好的选择。 |
|
说明
constexpr 主要用于修饰函数返回值,表明该函数的返回值是编译时已知的,意即:此函数内仅包含一条形如“return
常量表达式;”这样的语句。constexpr 同样也可以用来修饰对象,如果用它来修饰用户自定义类型,则用户需要为该类型配备
constexpr 构造函数,该构造函数同样仅允许使用编译时已知的常量完成成员初始化。 |
优点
缺点
总结
|
说明
POD(Plain Old Data)类型指那些布局与 C struct 兼容的用户自定义类型。C++03 中对 POD
类型的定义过于严苛,在新标准中,基本上,只要一个类不使用虚表和虚基类;只使用默认的构造和析构函数,并且其所有基类和成员也都如此的话,这个类的布局就是
POD 兼容的。新标准取消了所有成员都必须是 public 访问权限等没有意义的限制条件。 |
优点
缺点
总结
事实上,貌似从 C++ 诞生开始,广大编译器厂商就一直在使用上述放松标准了。因此这一改动只不过是顺应事实之举,对现有
C++ 程序员来说没有任何影响。 |
|
说明
通过使用 extern
来修饰一个模板类,告知编译器该模板无需在当前编译单元内实例化(必要的实例化已在其它编译单元内进行)。 |
优点
- 提供对于程序员来说,更明确,与类型实例化(外部变量和对象定义)看上去更相似的模板显式实例化方式。
|
缺点
总结
大部分近代 C++
编译器都已支持模板的显式实例化。甚至不少编译也早已允许在模板显式实例化语句前面加个“extern”修饰词了。因此跟上一条一样,这也是属于“先上车后补票”的勾当。 |
|
说明
优点
- 对于很多容器类型来说(比如:std:vector),可大大方便其创建过程。
|
缺点
总结
好用的功能,很适合经常需要通过初始化列表来构造容器对象的场合。 |
|
说明
无论是 POD 还是非 POD 类型都可以使用 obj = { ... } 的方式来进行初始化。对于非 POD 类型 {
... } 将自动匹配和调用构造函数。因此,构造函数 T(x, y) 现在也可以写成:T{x, y}、T var{x, y}
等。在需要返回一个 T 类型对象时,甚至可以直接写:“return {x, y};” |
优点
- 一致的构造方式便于编译器和辅助工具完成语法解析。
- 可以灵活地构造和创建对象。
|
缺点
- 滥用类似“return {x, y};”形式的自动类型推断很容易降低代码易读性和可维护性。
|
总结
|
说明
在 C++11 中,auto
关键字的语义有所更改,从原来的声明一个存活于当前调用栈上的自动变量,转变成了通过表达式的右值来自动推断左值的类型。decltype
关键字则用于判断指定表达式的类型。 |
优点
- 在定义模板,或需要获得 Lambda 表达式等匿名对象的类型时,可提供方便。
|
缺点
- 在模板中,应只在类型参数未知或很难获得的时候才应考虑使用上述机制。否则很容易极大地降低代码易读性和可维护性。
- 类似地,在普通代码中,只应在操作匿名类型对象时,才使用上述机制。
- 虽然在 C++03 中,auto
关键字很少用到。但也不可避免的带来了兼容性问题——很少用到毕竟不等于完全没用到。
|
总结
|
说明
没啥好多说的,就是现如今已快烂大街的 foreach 类操作了。对一些特殊容器和大部分守规矩(支持迭代子和
begin()、end() 调用)的容器类型都有效。 |
优点
缺点
总结
随大流的 for 语句便捷写法。没有它日子也能过,有了确实会方便些,但也程度有限。 |
|
说明
可在表达式中定义一个匿名函数对象,并返回它的实例。对闭包(closure)的简单解释:一段可调用的代码加上它执行时所依赖的上下文。在
C++11 中,闭包也是通过匿名函数对象的自动生成来实现的,例子:
std::vector<int> some_list;
int total = 0;
int value = 5;
std::for_each(some_list.begin(), some_list.end(), [&, value, this](int x) {
total += x * value * this->some_func();
});
|
相当于:
class FOUnnamed {
private:
int& m_rnTotal;
int m_nValue;
CEnv* m_pEnv;
public:
FOUnnamed(int& total, int value, CEnv* this)
: m_rnTotal(total), m_nValue(value), m_pEnv(this)
{}
void operator()(int x)
{
m_rnTotal += x * m_nValue * m_pEnv->some_func();
}
};
std::vector<int> some_list;
int total = 0;
int value = 5;
FOUnnamed foPred(total, value, this);
std::for_each(some_list.begin(), some_list.end(), foPred);
|
因此可以认为 lambda 表达式和闭包是一种生成匿名函数对象的快捷方式。 |
优点
缺点
- 仅适用于封装只有几行代码的简单函数对象。如果滥用则很容易极大地降低代码易读性和可维护性。
|
总结
|
说明
类似 lambda 表达式中的返回值定义方式,模板和普通函数也可以使用类似的语法指定返回值,例如:
template<class Lhs, class Rhs>
auto adding_func(const Lhs &lhs, const Rhs &rhs) ->
decltype(lhs+rhs) {return lhs + rhs;} |
显而易见,这种语法主要用来帮助完成模板定义时的类型推断——在上例中,由于用来推断返回类型的“lhs”和“rhs”在“adding_func(...)”之前还未定义,因此必须将返回值类型定义延迟到参数列表之后。
应注意到,使用这种后置语法时,函数原来指定返回值类型的位置必须记做“auto”。 |
优点
缺点
总结
要么在整个项目中一致地使用,要么仅作为一种解决特定问题的手段,在“被逼无奈”时才使用。 |
|
说明
在 C++11
中,一个构造函数可以调用该类中的其它构造函数来完成部分初始化任务(委托)。声明成员时可以直接指定默认初始值,例如:
class CSomeType {
int m_nNumber;
int m_nValue = 10;
public:
CSomeType(int new_number) : m_nNumber(new_number) {}
CSomeType() : CSomeType(42) {}
}; |
|
优点
- 可在一定程度上简化初始化相关代码。
- 比起将构造的公共部分封装为一个私有方法供所有构造函数调用,委托更便于编译器实现一些优化。
|
缺点
- 在 C++03
中,如果多个构造函数需要使用同一段初始化代码,亦可将这段代码封装为一个私有方法供所有构造方法调用。
- 委托的加入改变了构造的语义:C++03
中,当一个对象的构造函数返回时,该对象构造完成;而在新语义中,仅当一个对象的所有构造函数调用都返回后,才表明该对象构造完成,使得对象构造过程被进一步地复杂化。
-
类似地,基类和成员的构造语义也被相应地复杂化:仅当基类中的所有构造函数调用均返回时,派生类成员才开始构造;当成员中的所有构造函数均返回后,派生类才开始构造……
|
总结
利大于弊,值得一试。但要搞清楚新的语义,还要考虑好构造函数中的异常抛出与处理方式。 |
|
说明
override
修饰符用于标识指定的虚方法重载了基类中的同名虚方法——而不是定义一个新的同名虚方法(比如仅参数列表不同的虚方法)。该修饰符导致编译时的严格检查,避免因为函数签名不同而重载失败。
final 修饰符可用于修饰类或方法。在修饰类时,它表示指定类是一个 concrete 类——不能再作为基类而被其它类继承。
将
final 修饰符作用于方法时,表示此虚方法已经是最终实现,任何在派生类中重载这个方法的企图都将引发一个编译错误。 |
优点
-
定义和派生抽象类的利器,可在编译时发现诸如:修改了基类中某虚方法的参数列表后,忘记在其派生类中做出相应修改等各类相关错误。
- 使得类和虚函数定义的语义更清晰,有利于增强可读性。
- 为编译器提供了更多的优化线索。
- override 和 final
只是修饰符,不是关键字。它们除了在特定上下文中有特别含义外,仍然可以作为合法的标识符使用。
|
缺点
总结
|
说明
C++11 中定义了语言级的 NULL 指针常量:nullptr。 |
优点
- 比起我们自定的“NULL”宏来说,nullptr 常量更利于函数重载。
|
缺点
总结
无论是 NULL 还是 nullptr,都应尽可能一致地使用(尽量避免混用)。 |
|
说明
C++11 通过“enum class
...”的语法为枚举提供了强类型支持,同时还允许用户指定枚举的具体类型,如:16位无符号整形;32位整形;64位整形等等。 |
优点
- 强类型检查杜绝了不同枚举类型之间的比较或枚举与整形之间的比较,能够在编译时发现更多错误。
- 可指定枚举的位宽大大方便了很多原来不方便使用枚举的场合(例如:可以打破 32 位限制、可以显式强制保证 8
位尺寸等等)。
|
缺点
总结
|
说明
定义模板实例时,不再需要小心地在多个连续的大于号之间添加空格了。 |
优点
缺点
总结
|
说明
在 C++11 中,explicit 修饰符已可应用于类型转换操作,帮助编译器更好地完成类型转换和函数重载判定。 |
优点
-
一个与转换操作的类型严格匹配的场合可以自动应用显式类型转换,这使得显式类型转换操作可以在不降低易用性的前提下,提供更好的类型匹配。
|
缺点
总结
基本上也属于是早就该有的特性,相信很多程序员都试着写过“explicit operator bool()”、“explicit
operator INT64()”之类的东东。 |
|
说明
可以使用 using 语法来定义模板实例的别名。效果跟 typedef 基本一样。 |
优点
缺点
- 官方的说法是 C++03 不允许通过 typedef 定义包含整形模板参数的实例。例如:“typedef
CHandle< T, DestoryDeletor<T>, CAtomicINT32, NULL >
HT;”语句按照 C++03 标准来说是非法的(假设“NULL”被定义为 0)。using 变种主要就是用来解决这个问题的。但实际上我已经在各种版本的 GCC、VC、Intel C 等编译器上这么用了很多年啊很多年。
因此可以认为这个功能就等同于 typedef。
|
总结
意思不大,继续用 typedef 就好。如果觉得 using 更别致,那么就请一致地使用(尽量避免混用)。 |
|
说明
在 C++11 中 union 中已可包含部分非 POD 类型,也可以给 union 定义构造函数和方法了。 |
优点
- 放宽了 union 对部分非 POD 成员的限制无疑会使其更有用。
- 允许为其定义构造和方法进一步方便了使用。
|
缺点
- 无论如何,由于 union 的先天性质,还是很难让其支持析构方法吧?
- 其先天性质决定了,一个复杂 union 结构的可读性和可维护性恐怕都不怎么样。
|
总结
好用的特性,但要注意适可而止,不要把设计搞的太复杂。 |
|
说明
为模板方法提供了类似普通函数的可变数量参数(...)支持。 |
优点
- 可极大地方便一些特殊模板的构建,例如:用于实现智能指针模板类中的“operator->*()”(成员指针解引用)操作,以及一些类似于“printf”的模板方法。
|
缺点
总结
|
说明
C++11 中新增了 char16_t 和 char32_t 两中类型,用以应对 wchar_t
位宽不确定的问题。又新增了 UTF-8(u8)、UTF-16(u)和
UTF-32(U)字符串字面值定义方法。同时还新增了一种无需对引号(")和反斜杠(\)等特殊字符进行换码的裸字符串字面值定义方法(R),该方法还可以与前面提到的 UTF 字面值前缀结合使用(u8R, uR, UR),非常全面和实用。 |
优点
- 明确了 UTF-8、UTF-16 和 UTF-32 字符类型及其字面值定义方法。
- 对于经常要写些 HTML/XML 或者正则表达式之类字面值的童鞋来说,裸字符串定义方式可以免除换码的烦恼。听上去没啥,但是等 coding 的时候……那是谁用谁知道呀。
|
缺点
总结
|
说明
就像“1000ul”表示“值为 1000 的无符号长整数”字面值一样,C++11 允许用户通过新的“operator
""”操作自定义字面值后缀解释器,例如:
CMyClass operator "" _mysuf(const
char* literal_string);
CMyClass iVar = 1234_mysuf; |
|
优点
- 用户可以方便地直接通过字面值来生成自定义类型的对象。
|
缺点
总结
这涉及到 coding 时方便与阅读/维护时方便之间的权衡,应慎用。 |
|
说明
新标准中,“thread_local”存储类可用来指定一个 TLS。 |
优点
- C++11 中规定任何可以使用“static”存储类的地方,都可以使用 thread_local。也就是说,可以用它来修饰带有构造、析构方法的对象,使得
TLS 用起来非常简便。
|
缺点
- 从 thread_local 语义到实际操作系统 TLS Slot API 之间的封装和映射可能带来额外的运行时开销。
|
总结
好用的东东。比起直接使用操作系统的 TLS Slot 虽然可能增加少许隐式的开销,但通常对 TLS
存取也没有太高的效率要求(当然至少要比使用互斥量等锁算法要高效才行)。 |
|
说明
C++11 中,用户可以通过“= default”后缀修饰符,为每个类显式指定默认构造函数。也可以通过“=
delete”后缀修饰构造函数和赋值等操作(通常用来实现禁止复制的语义)。 |
优点
- 能够通过明确的方式显式限定这些特殊方法有助于增强代码的可读性和可维护性。
|
缺点
总结
是传统的,通过将拷贝构造和赋值操作声明为 private 来禁止复制或类似做法的最佳替代品。 |
|
说明
C++11 中新增了确保至少 64 bit 的 long long 类型。 |
优点
缺点
总结
还有什么可说的呢?同样是一个早就该有,而且各大编译器早已支持的特性。 |
|
说明
类似 assert,但专为模板而生:不像 assert 在运行时才检查,static_assert 在模板实例化时执行。 |
优点
缺点
总结
|
说明
允许 sizeof 操作直接作用于类的成员,例如:“sizeof CMyClass::m_nValue”。 |
优点
缺点
总结
C++03 虽然不允许类似:“sizeof CMyClass::m_nValue”的写法,但也允许 sizeof 作用与对象的成员,例如:“CMyClass
iTest; return sizeof(iTest.a)”之类。而且除了事先不知道类型的模板以外,还真没什么地方需要这么用
sizeof 的。 |
|
|