18.1.1 新类型
C++11 新增了类型 long long 和 unsigned long long 以支持 64位(或更宽)的整型;
新增了类型 char16_t 和 char32_t ,以支持 16位 和 32位的字符表示; 还增加了 “原始” 字符串。
18.1.2 统一的初始化
C++11 扩大了用大括号括起来的列表(初始化列表)的适用范围,使其可用于所有内置类型和用户定义的类型(即类对象)。
适用初始化列表时,可添加等号(=),也可不添加。
int x = {5};
double y {3.14};
short quar[5] {1, 2, 3, 4, 5};
int * arr = new int[4] {2, 4, 6, 8};
//创建对象时
class Stump
{
private:
int roots;
double weight;
public:
Stump(int r, double w) : roots(r), weight(w) {}
};
Stump s1(1, 11.1); //old style
Stump s2{2, 22.2}; //C++11
Stump s3 = {3, 33.3}; //C++11
1.缩窄(防止缩窄)
初始化列表语法可防止缩窄,即禁止将数值赋给无法存储它的数值变量。
char c1 = 1.55e27; // double-to-char, undefined behavior
char c2 = 45968241; // int-to-char, undefined behavior
//禁止缩窄的类型转换
char c3 {1.55e27}; //double-to-char, compile-time error
char c4 = {45968241}; //int-to-char, out of range, compile-time error
//允许转换为更宽的类型
char c5 {66}; //int-to-char, in range, allowed
double c6 = {66}; //int-to-double, allowed
2.std::initializer_list
C++11 提供了模板类 std::initializer_list, 可将其用作构造函数的参数。
然而,如果类有将模板 std::initializer_list 作为参数的构造函数,则只有构造函数可以使用列表初始化形式。
列表中的元素必须是同一种类型或可以转换为同一种类型。
// STL 容器提供了将 std::initializer_list 作为参数的构造函数
vector<int> a1(10); // uninitialized vector with 10 elements
vector<int> a2{10}; // initializer-list, a2 has 1 element set to 10
vector<int> a3{1, 2, 3}; // 3 elements set to 1, 2, 3
/*
* 头文件 initializer_list 提供了对模板类 initializer_list 的支持。这个类包含成员函数 begin() 和 end(),
* 可用于获取列表的范围。除用于构造函数外,还可将 initializer_list 用作常规函数的参数:
*/
#include <initializer_list>
double sum(std::initializer_list<double> i1);
int main()
{
double total = sum( {2.5, 3.1, 4} ); // 4 converted to 4.0
……
}
double sum(std::initializer_list<double> i1)
{
double tot = 0;
for (auto p = il.begin(); p != il.end() ; p++)
tot += p;
return tot;
}
18.1.3 声明
1> auto (自动类型推断)
auto maton = 112; // maton is type int
auto pt = &maton; // pt is type int *
double fm(double, int);
auto pf = fm; // pf is type double (*) (double, int)
// auto 简化模板声明
for (std::initializer_list<double>::iterator p = il.begin(); p != il.end(); p++)
//可替换为:
for (auto p = il.begin(); p != il.end(); p++p)
2> decltype (将变量的类型声明为表达式指定的类型)
decltype(x) y; // 让 y 的类型与 x 相同, x 是一个表达式
double x;
int n;
decltype(x*n) q; // q same type as x*n, i.e., double
decltype(&x) pd; // pd same as &x, i.e., double *
//定义模板时特别有效,因为只有等到模板被实例化时才能确定类型
template<typename T, typename U>
void ef(T t, U u)
{
decltype(T*U) tu;
… …
}
// 其中 tu 将为表达式 T*U 的类型,这里假定定义了运算 T*U 。 例如,如果 T 为 char , U 为 short , 则 tu 将为 int 。
// 指定的类型可以为 引用 和 const
int j = 3;
int &k = j;
const int &n = j;
decltype(n) i1; // i1 type const int &
decltype(j) i2; // i2 type int
decltype((j)) i3; // i3 type int &
decltype(k+1) i4; // i4 type int
3> 返回类型后置
C++11 新增了一种函数声明的语法:在函数名和参数列表后面(而不是前面)指定返回类型:
double f1(double, int); // traditional syntax
auto f2(double, int) -> double; // new syntax, return type is double
//优点:可以使用 decltype 来指定模板函数的返回类型
template<typename T, typename U>
auto eff(T t, U u) -> decltype(T*U)
{
. . .
}
// 这里解决的问题是:在编译器遇到 eff 的参数列表前, T 和 U 还不在作用于内,因此必须在参数列表后使用 decltype
4> 模板别名:using=
typedef std::vector<std::string>::iterator itType;
//C++11 提供了另一种创建别名的方法
using itType = std::vector<std::string>::iterator;
// typedef 与 using 区别: using 可用于模板部分具体化, 而 typedef 不能
template<typename T>
using arr12 = std::array<T, 12>; // template for multiple aliases
// 上述语句 具体化 目标板 array<T, int>(将参数 int 设置为 12)。
std::array<double, 12> a1;
std::array<std::string, 12> a2;
//可将它们替换成:
arr12<double> a1;
arr12<std::string> a2;
5> nullptr
简言之:使数据各司其职 之前 0 可以表示 int , 同时 0 还可以表示 空指针, nullptr 专一的表示空指针 , 为了兼容 代表空指针的用法不变 。
所以 表达式 nullptr == 0 为 true, int 传参时可以体现出来, 传0没问题 ,但是 传 nullptr 就错了 ,这反映了 使用 nullptr 而不是 0 提供了更高的类型安全。
18.1.4 智能指针
C++11 摒弃了 auto_ptr , 并新增了三种智能指针:unique_ptr , shared_ptr 和 weak_ptr。
18.1.5 异常规范方面的修改
之前
void f501(int) throw(bad_dog); // can throw type bad_dog excetpion
void f733(long long) throw(); // doesn't throw an exception
与 auto_ptr 一样,C++ 编程社区的集体经验表明,异常规范的效果没有预期的好。因此,C++ 摒弃了异常规范。然而,标准委员会认为,
指出函数不会引发异常有一定的价值,他们为此添加了关键字 noexcept:
void f875(short, short) noexcept; //doesn't throw an exception
18.1.6 作用域内枚举
//由 class 或者 struct 定义。 解决了同一个作用域内 枚举成员同名的问题, 移植性问题 以及 类型检查低级问题
enum Old1 {yes, no, maybe}; // traditional form
enum class New1 {never, sometimes, often, always}; // new form
enum struct New2 {never, lever, sever}; // new form
//新枚举要求进行显示限定,以免发生名称冲突。因此,引用特性枚举时,需要使用 New1::never 和 New2::never 等
18.1.7 对类的修改
1> 显示转换运算符
// C++ 引入了关键字 explicit , 以禁止单参构造函数导致的自动和转换
class Plebe
{
Plebe(int); // automatic int-to-plebe conversion
explicit Plebe(double); //requires explicit use
...
};
...
Plebe a, b;
a = 5; // implicit conversion, call Plebe(5)
b = 0.5; // not allowed
b = Plebe(0.5); // explicit conversion
// C++11 拓展了 ecplicit 的这种用法,使得可对转换函数做类似的处理
class Plebe
{
// conversion functions
operator int() const;
explicit operator double() const;
...
};
...
Plebe a, b;
int n = a; // int-to-plebe automatic conversion
double x = b; // not allowed
x = double(b); // explicit conversion, allowed
2>类内成员初始化
class Session
{
int mem1 = 10;
double mem2 {1966.54};
short mem3;
public:
Session(){}
Session(short s) : mem3(s) {}
Session(int n, double d, short s) : mem1(n), mem2(d), mem3(s) {}
};
// 注:可使用 等号 或者 大括号 版本的初始化,但不能使用 圆括号 版本的初始化。
// 构造函数 初始化列表提供的值 会将默认值 覆盖 (即:覆盖类内成员初始化)
1> 基于范围的 for 循环
double prices[5] = {4.1, 5.2, 6.3, 7.4, 8.5};
for (double x : prices) // 当然,这里的 double 可以用 auto 来替代
std::cout<<x<<std::endl;
std::vector<int> vi(6);
for (auto &x : vi)
x = std::rand(); // 循环中修改元素内容 可使用引用类型
2> 新的 STL 容器
新增 forward_list(单项链表) , unordered_map , unordered_multimap , unordered_set , unordered_multiset
还增加了模板数组 array std::array<int, 360> arr; //arr of 360 ints
3> 新的 STL 方法
cbegin() 和 cend(); 简言之,元素具有 const 属性
4> valarray 升级
5> 摒弃 export
6> 尖括号
std::vector<std::list<int> > v1; // 避免 >> 与 > > 混淆
std::vector<std::list<int>> v1; // ok in C++11
18.1.9 右值引用
C++11新增了右值引用,使用 && 表示。
右值引用可关联到右值,即可出现在赋值表达式的右边,但不能对其应用地址运算符的值。右值包括字面常量(C-风格字符串除外,他表示地址)、诸如 x+y 等表达式以及返回值的函数(条件是该函数返回的不是引用):
int x = 10;
int y = 23;
int && r1 = 13;
int && r2 = x + y;
double && r3 = std::sqrt(2.0);
注意:r2 关联到的是 x+y 得到的结果(即 33),即使 修改了 x 或者 y 的值,也不会影响到 r2。
有趣的是,将右值关联到右值引用导致该右值被存储到特定的位置,且可以获取该位置的地址。也就是说,虽然不能将运算符 & 用于 13, 但可将其用于 r1。 通过将数据与特定的地址关联,使得可以通过右值引用来访问该数据。参考如下代码:
double func(double tf) { return 5.0*(tf-32.0)/9.0; }
以下代码是在 XCode 下 init 函数 中写的代码 你完全可以 在 int main(){} 中写(如果 windows 下 支持的话)
double tc = 21.5;
double && rd1 = 7.07;
double && rd2 = 1.8 * tc + 32.0;
double && rd3 = func(rd2);
std::cout<<" tc value and address: "<<tc<<", "<<&tc<<std::endl;
std::cout<<"rd1 value and address: "<<rd1<<", "<<&rd1<<std::endl;
std::cout<<"rd2 value and address: "<<rd2<<", "<<&rd2<<std::endl;
std::cout<<"rd3 value and address: "<<rd3<<", "<<&rd3<<std::endl;
运行结果: