c++11的新标准
新类型
新增了long long 和 unsigned long long 两个类型(64位的整型)
char32_t and char16_t
- 原始字符串
统一的初始化
- 扩大了大括号初始化的使用范围,可添加等号也可以不添加,
- 列表初始化的时候也可以用在new表达式中
- 大括号括起来来调用类的构造函
1
2
3Stump s1(3, 15.6); //old style
Stump s2{5, 43,4}; // C++11
Stump s3 = {4, 32.1}; //C++11好处:
- 缩窄:防止将数值赋给无法存储它的数值变量,但允许准换为更宽的类型
- Std::initializer_list 有用该类型修饰构造函数的参数的时候,初始化列表只能用该构造函数。并且这个修饰也可以修饰普通函数的参数,用于接受用大括号修饰的参数列表,并保持内容类型的一致性
声明
c++11 提供了多种简化声明的方式
auto
decltype 将变量的类型声明为表达式的类型
1
2
3
4// 构造一个比较的匿名函数
auto cmp = [](int left, int right) { return (left ^ 1) < (right ^ 1);};
// 声明 优先级队列的时候, 简化具体化模版类的类型的输入
std::priority_queue<int, std::vector<int>, decltype(cmp)> q3(cmp);返回类型后置
1
2double f1(double, int); // traditional syntax
auto f2(double, int) -> double; // new syntax, return type is double帮助模板函数指定返回类型,帮助编译器在遇到参数列表后,再去指定根据参数类型指定返回类型。
模板别名:using =
对于冗长的标识符,方便的创造别名。
以前:typedef
1
typedef std::vector<std::string>::iterator itType;
C++11提供的方式:using =
1
using itType = std::vector<std::string>::iterator;
差别在于,新语法可以用于模板的具体化
1
2template<typename T>
using arr12 = std::array<T, 12>;nullptr
c++11 新增了关键字nullptr,用于表示空指针,这样可以避免以前0即表示空指针,又表示整数常量的问题。
智能指针
自动的完成delete的工作。首先引入了auto_ptr,以帮助自动化的完成这些工作,发展过程中,需要一些更加精致的机制,新增了三个智能指针
unique_ptr
、shared_ptr
和weak_ptr
。当指针过期的时候,期西沟函数将调用delete函数的运行来释放内存,防止内存泄漏。使用智能指针
- 导入memory都文件
#include <memory>
- 使用智能指针的模板具体化实例指向新生成的对象
unique_ptr<string> ps = new string("test");
- 不需要delete去删除生成的内存
- 对于非堆内存是禁止使用智能指针去删除对应的内存的
string vacation("what a happy day!!!")
- 导入memory都文件
为什么有三种智能指针
当相同类型的智能指针相互赋值的时候,一块内存就会被删除多次,所以这样有很多种策略
- 执行深复制;
- 建立所有权(ownership)的概念,只有一个指针可以拥有它,这样只有拥有对象的智能指针的构造函数会删除该对象。然赋值操作去转让所有权。转让所有权之后该智能指针就不指向该内存了,就为空了,之后访问就会出现问题,auto_ptr就没有unique_ptr那么的严格;
- 跟踪和引用特定对象的智能指针数,这里是引用计数,当最后一个指针过期时,才调用delete。
为什么unique_ptr要优于auto_ptr?
- 当不合格使用unique_ptr的赋值的时候,会报错,而auto_ptr不会,运行时会发生错误
- 作为临时右值赋给函数返回的临时变量时,unique_ptr不会报错
1
2
3
4
5
6using namespace std;
unique_ptr<string> pu1(new string " Hi bo!");
unique_ptr< string> pu2;
pu2 = pu1; // not allowed;
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string "Yo!"); // allowed- unique_ptr支持使用std::move(),安全的执行智能指针的赋值操作
如何使用智能指针?
- 对个指向同一对象的指针 使用shared_ptr
- 不需要指向同一个对象的指针 使用unique_ptr
- new分配内存时,才能使用 auto_ptr 和 shared_ptr。 new [] 和 new 分配内存的时候都可以使用 unique_ptr。 当不再内存分配内存时,不能使用智能指针
c++11 抛弃了异常规范的指定,并且添加了关键字noexcept,指出不会引发异常的关键
作用域内的枚举
不同的实现采用不同的底层类型,之前的底层类型都是整形类型。
枚举名的作用域为枚举定义所属的作用域,所以相同的作用域内的两个枚举,枚举变量名不能是相同的枚举名。新的枚举使用class或struct定义
对类的修改
显示转换运算符
原因:自动类型转换可能引起各种问题,c++引入老人关键词explicit,以静止单参数构造函数导致的自动类型转换
1
2
3
4
5
6
7
8
9
10class Plebe{
Plebe(int); //automatic int-to-plebe conversion
explicit Plebe(double); // required explicit use
...
};
Plebe a, b;
a = 5; //implicit conversion, call Plebe(5)
b = 0.5; // not allowed
b = Plebe(0.5); // allow explicit conversionc++11拓展了explicit的用法,使得可对转换函数做类似的处理
类内成员的初始化
在类内定义中初始化成员
形式:
1
2
3
4
5
6
7
8
9class 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) {}
}好处:使用该初始化方式可以有效的减少代码量
模板和STL方面的修改
基于范围的for的循环
新的STL的容器
- forward_list 单向链表
- unordered_map,unordered_set 以及对应的multimap 或者multiset
- 新增的模板array
valarray升级
尖括号
需要和运算符>>区分开,所以声明嵌套的模板时,需要使用空格将尖括号分开
右值引用
传统的c++引用,现在被称为左值引用,使得标识符关联到左值。(表示数据的表达式,变量名 或解除引用的指针),程序可获取其地址。
C++11新增了右值引用,这是使用&&表示的。右值引用关联到右值,即可以出现在赋值表达式右边,但不能对其应用地址运算符的值。右值包括字面常量(c风格字符串除外,它表示地址)、诸如x+y等表达式以及返回值得函数(条件是该函数返回的不是引用):
1
2
3
4
5int x = 10;
int y = 23;
int && r1 = 13;
int && r2 = x + y;
double && r3 = std::sqrt(2.0);引用右值引用的主要目的就是用于移动语义
移动语义和右值引用
为何需要移动语义?
移动语义避免了移动原始数据,而只是修改了记录,由于右值引用是临时变量,右值引用才可以引用该临时能量。该临时能量的值可以直接被窃取而返回,所以减少移动原始数据。
1
2
3Useless two = one; //calls copy constructor
Useless four(one + three); //临时变量作为输入,由于声明了移动构造函数,所以可以直接调用移动语义 calls move constructor在之前没有改移动语义的出现,是编译器的智能编译实现的优化
- static_cast<>可将对象的类型强制转换为右值,转换为右值后可以使用移动构造函数和移动赋值运算符
- 使用移动构造函数和普通的复制构造函数的主要区别是,输入变量是左值还是右值,左值还可以被调用,所以还有用处,而右值一般是临时的,不在被调用的,存储在一个临时的特殊位置,所以一般不会被再次调用,所以可以将其直接转移,减少复制。而static_cast<>可以将左值转换为右值。
- C++11 提供了更简单的std::move()的方式,在头文件utility中。
新的类功能
- C++11新增了移动构造函数和移动赋值运算符
可以使用默认的成员函数: default 或者禁用默认的成员方法: delete
管理虚方法的标识符: override 和 final
override
1
2
3virtual void f(char* ch) const override {
std::cout << val() << ch << "!\n";
}使用虚说明符override指出您要覆盖一个虚函数:如果声明出现问题,不能覆盖基类的虚函数的话就会产生报错
final
禁止派生类覆盖特定的虚方法,就会使用final虚函数说明符,放在虚函数的末尾
Lambda函数
比较函数指针、函数符和Lambda函数
首先将这三者都可以统称为函数对象。举例说明,完成一个函数对象,该函数对像需要返回该整数是否被可以被指定的整数整除:
函数名:
1
2bool f3(int x) {return x % 3 == 0;}
int count3 = count_if(numbers.begin(), numbers.end(), f3); //指定函数对象为函数名函数符:
函数符是一个类对象,重载operator()()方法来实现一些功能。
好处:可以使用同一个函数符来完成两项的计数的任务
1
2
3
4
5
6
7
8
9
10
11
12
13class f_mod {
private:
int dv;
public:
f_mod(int d = 1): dv(d) {}
bool operator()(int x) {return x % dv == 0};
}
f_mod obj(3);
bool is_div_by_3 = obj(7); // 运行重载之后的operator() () 函数
int count3 = std::count_if(number.begin(), number.end(), f_mod(3));Lambda函数
c++11中对于接受函数对象的函数,可以使用匿名函数定义其参数。
1
int count3 = std::count_if(numbers.begin(), numbers.end(), [](int x){return x % 3 == 0;})
区别:
使用[]替代了函数名;
没有声明返回的类型, (相当于隐性的使用了decltyp得到的类型)
仅当lambda表达式完全又一条语句构成的时候,才可以不指定返回类型,比如上面的情况;否则需要使用新增的返回类型后置的语法
1
[](double x)->double{int y = x; return y - x;} // 返回类型后置
- 为什么要使用Lambda
- 距离: 让定义位于使用的地方
- 简洁: 函数符代码比函数名和lambda代码更加繁琐
- 效率: 函数地址的方法以为着是非内联的函数,而函数类型和lambda函数不会阻止内联
- 功能:lambda函数可以访问作用域内的任何动态变量,要捕获要使用的变量,将其名称放入中括号内。
- 如果只指定了函数名, 如[z],将按值访问变量;
- 名称前加上&,如[&count], 将按引用的访问count;
- [&],按引用访问所有的变量
- [=], 按值访问所有的动态变量
- 也支持混合使用
包装器 — 引申出适配器模式
c++提供对个包装器。这些对象用于给其他编程接口提供更一致或更合适的接口。C++提供不同的包装器模板,bind、mem_fn和reference_wrap以及包装器function。
包装器functiong和模板的低效性
function模板
1
std::function<double(char, int)> fdci;
从调用特征表的角度定义了一个对象,可用于包装调用特征标相同的函数指针、函数对象和lambda表达式。
1
2function<double(double)> ef1 = dub;
function<double(double)> ef2 = square;可以将function<double(double)>作为函数的形参,让形参f的类型与原始实参相匹配
c++11中的四种类型转换
static_cast<>
定义: Converts between types using a combination of implicit and user-defined conversions.
完成编译器允许的隐式类型转换
基本使用范围:
- 基本数据类型之间的转换
- 派生类体系的向上转型
example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
struct B {
int m = 0;
void hello() const {
std::cout << "Hello world, this is B!\n";
}
};
struct D : B {
void hello() const {
std::cout << "Hello world, this is D!\n";
}
};
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
int main()
{
// 1: initializing conversion
int n = static_cast<int>(3.14);
std::cout << "n = " << n << '\n';
std::vector<int> v = static_cast<std::vector<int>>(10);
std::cout << "v.size() = " << v.size() << '\n';
// 2: static downcast
D d;
B& br = d; // upcast via implicit conversion
br.hello();
D& another_d = static_cast<D&>(br); // downcast
another_d.hello();
// 3: lvalue to xvalue
std::vector<int> v2 = static_cast<std::vector<int>&&>(v);
std::cout << "after move, v.size() = " << v.size() << '\n';
// 4: discarded-value expression
static_cast<void>(v2.size());
// 5. inverse of implicit conversion
void* nv = &n;
int* ni = static_cast<int*>(nv);
std::cout << "*ni = " << *ni << '\n';
// 6. array-to-pointer followed by upcast
D a[10];
B* dp = static_cast<B*>(a);
// 7. scoped enum to int or float
E e = E::ONE;
int one = static_cast<int>(e);
std::cout << one << '\n';
// 8. int to enum, enum to another enum
E e2 = static_cast<E>(one);
EU eu = static_cast<EU>(e2);
// 9. pointer to member upcast
int D::*pm = &D::m;
std::cout << br.*static_cast<int B::*>(pm) << '\n';
// 10. void* to any type
void* voidp = &e;
std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}dynamic_cast
定义:Safely converts pointers and references to classes up, down, and sideways along the inheritance hierarchy.
用法:dynamic_cast <
new_type
> (expression
)If the cast is successful,
dynamic_cast
returns a value of typenew_type
.If the cast fails and
new_type
is a reference type,it throws an exception that matches a handler of type std::bad_cast.const_cast
定义:对指针或引用去除或者添加const属性
example:
1
2
3
4
5
6
7const int a= 0;
int b = const_cast<int>(a);//不对的
const int *pi = &a;
int * pii = const_cast<int *>pi;//去除指针中的常量性,也可以添加指针的常量性;reinterpret_cast
定义: 无视类型的引用之间的转换