C++11的部分新特性解析
2016-04-03 17:25
435 查看
C++11的部分新特性解析
1. 声明参数的简写形式int i = 0;可写为
int i(0);
string s = “123”;可写为
string s{“123”};
int a[3] = {1,2,3};可写为
int a[3]{1,2,3};
2. For循环的简写形式
例:
输出int list[10]中的每个元素。
原写法
for (int i(0); i < 10; i++) cout << list[i] << endl;
新写法
for (int i : list) cout << i << endl;
两种写法完全等价。
3. 自动类型
声明变量的时候,如果是声明+赋值的形式,则可以将类型一律声明为auto,而无须指定具体的类型。
例:
auto a = 1; auto b = 1.6;
此处a的类型由后面1的类型决定,为int。
而decltype可以理解为auto的反函数,获取类型。
例:
用a的类型声明b。
auto a = 1; decltype(a) b;
4. Tuple(元组)
Tuple可以理解为,将任意个数的完全不同的类型的变量放在同一个数组中。
可以类比python中的tuple,基本一致。可以参考我的另一篇文章 C++中使用tuple 。只将重点列出来。
例:
生成tuple
auto tup1 = std::make_tuple("Hello World!", 'a', 3.14, 0);
上述代码创建了一个
tuple <const char*, char, double, int>类型的元组。
拆开tuple
auto tup1 = std::make_tuple(3.14, 1, 'a'); double a; int b; char c; std::tie(a, b, c) = tup1;
这样做的结果是
a = 3.14, b = 1, c = 'a'。
如果不想要某一位的值,可以直接将其用ignore代替。
std::tie(ignore, b, c) = tup1;
连接tuple
std::tuple<float, string> tup1(3.14, "pi"); std::tuple<int, char> tup2(10, 'a'); auto tup3 = tuple_cat(tup1, tup2);
输出第i个元素
std::tuple<float, string> tup1(3.14, "pi"); cout << get<i>(tup1);
5. Lambda语法
Lambda语法可类比python,在我的另一篇文章中有详细描述,C++中使用Lambda,可参考。只将重点列出来。
Lambda用于快速声明匿名的函数。格式如下:
1. 传递方式可以为空,&,=,或者为每一个外部参数指明传递方式。= 表示为值传递方式,& 表示为引用传递方式。就算为空,中括号也不可以省略。
2. 参数列表即函数的参数,参数可以按值或者按引用传递。如果没有参数,可以省略(包括小括号)。
3. mutable是可选的,如果声明了mutable则可以修改按值传递进来的参数的拷贝(在lambda表达式外修改不可见)。
4. throw也是可选的,用来抛出函数处理过程中的异常,需制定抛出的异常的类型。
5. 返回类型是函数返回值的类型,在两种情况下可以省略,在后面叙述。
6. 函数体,实现功能的部分。
例:
int add(int x, int y) { return x + y; }
与下面的lambda表达式等价
auto add = [](int x, int y) -> int { return x + y; };
区别是第二种声明方式可以声明在函数内部。C++是不允许函数嵌套的。
当然lambda并不是只能用来这样声明函数(这样声明反而更繁琐),而是用来配合一些新的语法使用。
6. 可配合Lambda的语法
for_each,可以类比python中的map,即对与数组中的每一个元素都执行定义的方法。
例1:
将一个10个元素的int数组,int list[10]中每个元素乘以2。
for_each(list, list + 10, [=](int &i) {i = i * 2; });
可以看出for_each是由3个参数组成的,第一个参数为指向数组起始位置的指针,第二个参数为指向数组末尾的指针,第三个参数就是使用lambda定义的函数。执行的时候,实际上是对于数组中的每一个元素都执行这一个函数。其中的输入参数i在实际执行时就是数组中的一个元素。
如果list是vector、list等具有迭代器的stl模板,则写法为
for_each(list.begin(), list.end(), [=](int &i) {i = i * 2; });
例2:
将一个10个元素的int数组,int list[10]赋值为1~10;
for_each(list, list + 10, [=](int &i) {i = &i - list + 1; });
find_if,用于查找指定条件的元素(返回指向第一个满足条件的元素的指针或迭代器)
例:
查找第一个偶数的位置
vector<int> a = { 1, 2, 3, 4 }; auto res = find_if(a.begin(), a.end(), [](int x) -> bool { return x % 2 == 0; });
find_if也是三个元素,对比参照for_each理解即可。
count_if,用于统计满足指定条件的元素的个数
例:
统计偶数个数
vector<int> a = { 1, 2, 3, 4 }; auto res = count_if(a.begin(), a.end(), [](int x) -> bool { return x % 2 == 0; });
参数与find_if完全一致。
copy_if,提取满足条件的所有元素
例:
提取a中所有偶数并复制到b中
vector<int> a = { 1, 2, 3, 4 }; vector<int> b(a.size()); auto res = copy_if(a.begin(), a.end(), b.begin(), [](int x) -> bool { return x % 2 == 0; }); b.resize(distance(b.begin(), res));
参数与find_if完全一致。
关于find_if,copy_if和count_if的更详细用法,可以关注我的另一篇文章[C++快速开发] 查询 。
7. 空指针nullptr
在c++11以前一般空指针用NULL,而NULL的定义是0。
在c++11以后,尽量用nullptr,而不要用NULL。nullptr就是一个特殊的类型,代表空指针,他不等于0,也不是int类型。
这是一个编程习惯的问题,并不是强制性要求。
因为这样可以解决一个可能的歧义问题。因为函数可以重载,所以可以这样声明。
void F(int a) { cout << a << endl; } void F(int *p) { cout << *p << endl; }
如果按照以前的定义方式,声明一个空指针,
int *p = NULL,那么调用F函数F(p)时会因为歧义而导致崩溃,因为NULL的定义就是0是int类型,那么参数该怎样被解释?是int还是int*?在某些编译器中执行时会有bug。如果改为使用nullptr则可以避免该问题。
8. 关键字final和override
override用于修饰虚函数,用override修饰的虚函数必须在派生类中进行重载,否则会报错。避免了以前可能错误使用未重载的虚函数而导致错误。
final与override作用完全相反,防止虚函数被重写。
9. 多线程
多线程可以参考我的另一篇文章 C++多线程 。下面只列出重点。
例:
创建线程并输出一句话
thread t([] {cout << "new thread!" << endl; }); t.join();
配合mutex可以执行锁操作。
例:
双线程对同一变量执行加一操作
mutex m; int count = 0; auto increase = [&] //定义增加函数 { m.lock(); //用于互斥访问的锁 count++; cout << "id: " << this_thread::get_id() << endl; m.unlock(); this_thread::sleep_for(chrono::milliseconds(100)); }; thread t1([increase] { for (int i = 0; i < 5; i++) increase(); }); thread t2([increase] { for (int i = 0; i < 5; i++) increase(); }); t1.join(); t2.join();
10. function与bind
function适用于解决以前函数指针的不安全问题。用于将一个可调用实体进行包裹,实现泛型函数的回调。
理解的时候,就把它当作函数指针的安全替代品。
例:
//函数 int Test(int a) { return a; } //仿函数 class Test2 { public: int operator()(int a) { return a; } }; //lambda auto Test3 = [](int a){ return a; }; //类成员函数 class TestClass { public: int Test(int a) { return a; } }; std::function< int(int)> F; int main() { F = Test; F(10); Test2 T; F = T; F(10); F = Test3; F(10); TestClass TClass; std::function< int(int)> TFunc = std::bind(&TestClass::Test, TClass, std::placeholders::_1); }
通过上面的例子,可以看出,其中所指的可调用实体可以为普通函数、lambda泛型函数、仿函数、类成员函数等。解释下bind,实际上是一种将类的成员函数绑定到类对象和参数的方法。placeholders是表示不绑定对象,后面的数字表示对象的位置。再给一个简单的例子,绑定普通函数。
int Test(int x, int y); auto func = std::bind(Test, 10, std::placeholders::_1); func(20);
执行
func(20)实际上相当于执行了
Test(10,20);
11. 右值引用
右值引用以&&表示。可以优化代码,减少时间。
可参考我的另一篇文章C++右值引用 。不再多余叙述。
使用C++11的新语法,做的回溯生成数独的算法,可参考 [C++] 回溯法生成数独 。
代码通过Visual Studio 2015测试。
相关文章推荐
- C++11 lambda 表达式解析
- CHEMKIN 4.1 与 CHEMKIN-Pro 4.5 C++ Visual Studio 解决方案
- ACM常用的C++头文件!
- C++第2次实验作业
- NYOJ 16 矩形嵌套
- hao—C++和Java从编译到运行的过程区别
- 那些C++牛人的博客
- strcpy的使用以及注意事项
- C++矩阵运算库
- C语言格雷码相关
- C语言(三眼怪物)
- C++变量初始化顺序
- 20160403_C++初始化列表与赋值
- Visual C++笔记
- 大数乘法(C语言实现)
- c++之函数特性 重载 内联 函数默认值
- c++ 和 C#类型对照
- 29.C语言字符串和字符数组
- STL学习笔记— —STL简述
- 二叉搜索树的一些操作的C++实现