微软将于2010年4月12日发布Visual Studio 2010正式版,对C++语言进行了修改,使其更加符合C++标准。本文将介绍对 C++ 语言的修改。我们来做一些分析。 在微软即将推出的Visual Studio 2010正式版中,它对C++语言做了一些改变。此前,51cto也报道过Visual Studio 2010中C++项目的升级问题。文章重点介绍了C++语言的一些变化。 拉姆达表达式 许多编程语言都支持匿名函数。所谓匿名函数,是指这个函数只有函数体而没有函数名。 Lambda 表达式是一种用于实现匿名函数的编程技术。它为编写匿名函数提供了简洁的函数语法。两者都是Visual Studio中的开发语言。 Visual Basic 和 Visual C# 很早就实现了对 Lambda 表达式的支持。最后,Visual C++ 这次也不甘落后,在 Visual Studio 2010 中添加了对 Lambda 表达式的支持。 Lambda 表达式允许在使用函数的地方定义函数,并且可以在 Lambda 函数中使用 Lambda 函数外部的数据。这给设置操作带来了极大的方便。从功能上来说,Lambda表达式类似于函数指针和函数对象。 Lambda 表达式考虑了函数指针和函数对象的优点,而没有缺点。与函数指针或函数对象复杂的语法相比,Lambda表达式可以用非常简单的语法实现相同的功能,降低了Lambda表达式的学习难度,避免了使用复杂的函数对象或函数指针带来的问题。出现错误。我们可以看一个实际的例子: #include "stdafx.h" #include #include #include #include 使用命名空间 std; int _tmain(int argc, _TCHAR* argv[])   {   向量 v; for (int i = 0; i < 10; ++i) { v.push_back(i); }    for_each(v.begin(), v.end(), [] (int n) {   cout << n; if (n % 2 == 0) {   cout << “偶数”;   } else {   cout << “ 奇怪的 ”; } }); cout << endl;返回 0; } #include "stdafx.h" #include #include #include #include 使用命名空间 std; int _tmain(int argc, _TCHAR* argv[]) { 向量 v; for (int i = 0; i < 10; ++i) { v.push_back(i); }   for_each(v.begin(), v.end(), [] (int n) {  cout << n; if (n % 2 == 0) { cout << “偶数 ”; } else {  cout << “ 奇怪的 ”; } }); cout << endl;返回 0; } 修改be代码循环遍历输出向量中的每一个数,并判断这个数是奇数还是偶数。我们可以随意Lambda表达式而改变这个匿名函数的实现,修改集合的操作。在be代码中,C++使用范例中的“[]”来表示 Lambda 表达式的开始,其后面的“(int n)”表示 Lambda 表达式的参数。这些参数将在 Lambda 表达式中使用到。为了体会 Lambda 表达式的简单性,我们来看看如何使用函数对象实现相同的功能: #include "stdafx.h" #include #include #include #include 使用命名空间 std; struct LambdaFunctor { void operator()(int n) const { cout << n << " "; if (n % 2 == 0) { cout << "偶数"; } else { cout << "奇数"; } } }; int _tmain(int argc, _TCHAR* argv[]) { 矢量 v; for (int i = 0; i < 10; ++i) { v.push_back(i); for_each(v.begin(), v.end(), LambdaFunctor()); cout << endl ;返回0; #include "stdafx.h" #include #include #include #include 使用命名空间 std; struct LambdaFunctor { void operator()(int n) const { cout << n << " "; if (n % 2 == 0) { cout << "偶数"; } else { cout << "奇数"; } } }; int _tmain(int argc, _TCHAR* argv[] ) { 向量 v; for (int i = 0; i < 10; ++i) { v.push_back(i); for_each(v.begin(), v.end(), LambdaFunctor()) ; cout << endl;返回0; } 通过比较我们可以发现 Lambda表达式语法更加简洁,使用#p#更加简单高效 静态断言static_assert 在之前的C++标准C++03中,我们可以使用两种断言: ◆在预处理中使用条件编译和#error指令,可以在预处理阶段检查一些编译条件。 ◆可以使用宏assert进行运行时检查,保证程序逻辑的正确性。 但使用#error方法非常麻烦,而且无法检查模板参数,因为模板实例化是在编译时进行的,而#error方法是在预处理阶段进行的。断言宏在运行时检查。很容易看出,我们缺少的一件事是可用于在编译时进行检查的工具。于是,静态断言应运而生。 在新的C++标准C++0x中,增加了对静态断言的支持,并引入了新的关键字static_assert来表示静态断言。使用静态断言,我们可以在程序编译时检查某些条件是否成立。在调试模板函数的模板参数时,此功能特别有用。在编译期间,模板函数被实例化。这时,我们可以使用静态断言来测试模板函数的参数是否根据我们的设计有合适的值。例如,以下代码: template struct Kitten { static_assert(N < 2, "Kitten 需要 N < 2.");   };    int main() {   Kitten<1> 薄荷; Kitten<3> jazz; return 0; } template struct Kitten { static_assert(N < 2, "Kitten 需要 N < 2.");  };   int main() {  Kitten<1> 薄荷;小猫<3>爵士乐;返回0;} 当我们在main函数中使用“1”实例化Kitten结构时,编译时静态断言static_assert会测试参数N的值,当N的值小于2时,会产生断言错误,相应的调试帮助信息输出到“错误列表”窗口,以便程序员能够快速定位问题,更方便地解决问题。 此外,静态断言还带来许多其他优点。例如,静态断言在编译时进行处理,不会造成任何运行时空间和时间开销,这使得它比断言宏更加高效。另一个重要的特性是,如果断言失败,它会生成有意义且足够的诊断信息,帮助程序员快速解决问题。自动关键字 在 C++ 中,auto 关键字的含义发生了变化。从 Visual Studio 2010 开始,auto 关键字将用于指示编译器根据变量的初始值确定变量的数据类型。换句话说,我们可以将 auto 视为一种新的数据类型,它可以“从初始化器(初始化)派生出它所代表的变量的真实类型”。 auto 关键字的使用可以极大地消除当前替代方案产生的冗长且容易出错的代码。我们看一个实际的例子: #include  #include   #include   #include   #include   使用命名空间 std;使用命名空间 std::tr1; int main() {   map m; const 正则表达式 r("(\\w+) (\\w+)"); for (string s; getline(cin, s);) { 匹配结果; if (regex_match(s, results, r)) { m[结果[1]] = 结果[2]; }   }   for (auto i = m.begin(); i!= m.end(); ++i) {   cout << i->第二个<<“是”<< i->第一个<< endl; }   返回0; } #include  #include  #include  #include  #include  使用命名空间 std;使用命名空间 std::tr1; int main() { 地图m; const 正则表达式 r("(\\w+) (\\w+)"); for (string s; getline(cin, s);) { smatch 结果; if (regex_match(s, results, r)) { m[结果[1]] = 结果[2]; } } for (auto i = m.begin(); i!= m.end(); ++i) { cout << i->第二个<<“是”<< i->第一个<< endl; }  返回0; } 在becode中,我们使用关键字auto来代替了真正的数据类型map::iterator,这使得整个代码自然而简洁。另外,和其他数据类型一样,我们也可以修改auto关键字,比如添加const、指针(*)、左值引用(&)、右值引用(&&)等,编译器会根据auto类型进行修改。所代表的实际数据决定了这些修改的具体含义。为了兼容一些旧的C++代码,我们可以使用/Zc:auto编译器选项来告诉编译器是使用auto关键字原来的定义还是使用新标准C++中的定义。 #p# 右值引用 作为最重要的语言特性之一,右值引用被引入到 C++ 中。我们可以通过运算符“&&”声明右值引用。最初在 C++ 中使用“&”运算符声明的引用现在称为左值引用。 整数a; int& aa_lvref = a; // 左值引用 int b; int&& bb_rvref = b; // 右值应用 左值引用和右值引用的行为基本相同。它们之间的唯一区别是右值引用可以绑定到临时对象(右值),而左值引用则不能。例如: int& a_lvref = int();// 错误 C2440: '正在初始化' : 无法从 'int' 转换为 'int &' int&& b_rvref = int();// 好的! 在第一行代码中,我们将临时对象 int() 绑定到左值引用,这将产生编译错误。第二行,我们将临时对象绑定到右值引用上,编译就可以顺利通过。右值是无名数据。例如,函数的返回值通常是右值。对右值进行操作时,通常不需要保留右值本身,因此在某些情况下可以直接“移动”它。通过右值引用,程序可以清楚地区分传入的参数是否是右值,从而避免不必要的复制,提高程序的效率。我们考虑一个简单的小型数据交换程序来体验一下右值引用带来的效率提升。我们可以编写一个函数 swap 来交换两个变量的值: 模板 交换(T& a, T& b) { T tmp(a); // tmp 对象创建后,我们就有了 a 的两个副本 a = b; // 现在我们有 b 的两个副本 b = tmp; // 现在我们有两个 } 的副本 在这段代码中,虽然我们只是进行了简单的数据交换,但是我们却进行了多次对象的复制。对这些对象的复制操作,尤其是当这些对象比较大时,无疑会影响程序的效率。那么,如果使用右值引用,该如何实现呢? #include "stdafx.h" 模板 T&& move(T&& a) { return a; } 模板 void swap(T& a, T& b) { T tmp(move(a)); // 对象 a 被移动到对象 tmp,a 被清除 a = move(b); // 对象b被移动到对象a,b被清除 b = move(tmp); // 对象 tmp 被移动到对象 b } int _tmain(int argc , _TCHAR* argv[]) { int a = 1;整数 b = 2;交换(a,b);返回0; } #include "stdafx.h" 模板 T&& move(T&& a) { return a; } 模板 void swap(T& a, T& b) { T tmp(move(a)); // 对象a被移动到对象tmp,a被清除 a = move(b); // 对象b被移动到对象a,b被清除 b = move(tmp); // 对象 tmp 被移动到对象 b } int _tmain(int argc, _TCHAR* argv[]) { int a = 1;整数 b = 2;交换(a,b);返回0; } ​ 在这个重新实现的代码中,我们使用 move() 函数来替换对象赋值运算符“=”。 move() 只是接受一个右值引用或左值引用作为参数,然后直接返回对应的对象的右值引用。这个过程不会产生复制操作,而只是将源对象移动到目标对象。 复制和移动之间的差异使得右值引用成为 C++0x 中最令人兴奋的新功能之一。从实用的角度来看,它完全可以解决C++中长期被诟病的临时对象的效率问题。从语言本身来说,它改善了C++中引用类型在左值和右值方面的不足。从库设计者的角度来看,它为库设计者带来了另一个强大的工具。对于广大图书馆用户来说,甚至不用一兵一卒,就能获得“免费”的效率提升。在Visual Studio 2010中,由于支持这些新的C++功能,程序员对C++产生了兴趣。 【编辑精选】 C++指针漂移问题解决方法 C++标准输入输出应用技巧解析 C++托管程序实现安全管理 C++内存对齐详细使用指南 C++二维数组初始化相关应用技巧分享