您的位置:首页 > 编程语言 > C语言/C++

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测试。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: