65.9K
CodeProject 正在变化。 阅读更多。
Home

VS11 中原生开发的最新功能

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (53投票s)

2012 年 4 月 5 日

CPOL

11分钟阅读

viewsIcon

96588

在本文中,我将列举并讨论一些用于原生开发的新功能或改进功能(但并非全部)。

引言

Visual Studio 11 Beta 版于二月底发布。撇开普遍不喜欢的金属色主题不谈,新版本的 Visual Studio 为托管开发和原生开发都提供了许多改进。在本文中,我将列举并讨论一些用于原生开发的新功能或改进功能(但并非全部)。这些功能包括 C++11 语言支持、编译器和链接器、VC++ 库以及 IDE 中的改进或新增功能。功能的完整列表可在 MSDN 中找到。VS11 Beta 版可在此 下载。

不再支持 Windows XP

在讨论新增功能之前,我必须强调一个非常重要的问题:VS11 不再支持 Windows XP 和 Windows Server 2003。使用 VS11 构建的原生应用程序将不再能在 WinXP/Server2003 上运行,因为 VC++ 库(CRT 和 MFC)使用的是仅从 Vista 开始才可用的系统 API。最低支持的操作系统为 Windows Server 2008 SP2 和 Windows Vista。Microsoft Connect 上的一个 条目 针对此问题。

Mike Ryan 撰写了一篇博文,介绍了如何通过包装缺失的必需 API 并创建一个调用它们的汇编语言转发指令,使你的静态链接的 VS11 原生应用程序在 Windows XP 上运行。他的教程可在此 找到。

C++11 支持

VS11 扩展了 C++ 编译器对 C++11 的支持,但尚未完全实现。不过,他们承诺将在 VS11 发布后提供对核心功能更新的支持,可能以功能包的形式。VC++ Team 的这篇 博文 包含了一个 VS10 和 VS11 中 C++11 功能支持的表格。VS11 中的新增功能包括:

  • 前向声明的 enum 以及对强类型 enum 的完全支持
  • 标准布局和平凡类型
  • 基于范围的 for 循环
  • overridefinal
  • 并发功能:重写的序列点、原子操作、强比较并交换、双向栅栏和数据依赖顺序

强类型枚举

常规 C++ 枚举会将枚举值导出到周围的范围。这有两个缺点。首先,如果同一作用域中声明的两个不同 enum 中的枚举值名称相同,则可能导致名称冲突;其次,无法使用带完全限定名的枚举值,包括 enum 名称。这些问题在新标准中已得到解决,新标准提供了一种新的 enum 类型,通过 "enum class" 引入,称为强类型 enum。它们不再将枚举值导出到周围的作用域,可以拥有用户指定的整型基础类型(也为传统 enum 添加了此功能),并且不会隐式转换为 int

enum class Options {None, One, All};
Options o = Options::All;

enum 的前向声明允许在定义 enum 之前进行声明。

enum class Options;
void make_selection(Options* op);
// ... 
enum class Options {None, One, All};

可在此 找到一篇关于强类型 enum 的文章。

基于范围的 for 循环

在 C++ 中,对一系列值进行 (for) 循环操作一直不是非常方便。所有主流语言都有简化此操作的方法,但 C++ 没有。C++11 引入了基于范围的 for 循环,它具有用于迭代数组或容器的简单语法。如下所示:

int arr[] = {1,2,3,4,5};

for(int i : arr)
  cout << i << " ";

你甚至可以替换类型并使用 auto 关键字,这在处理 STL 容器时尤其有用(你可能会遇到类似 map<int, vector<string>> 的情况)。

for(auto i : arr)
   cout << i << " ";

在此示例中,变量 'i' 是只读的。你无法更改其值。如果要更改内容,可以使用引用。

for(auto &i : arr)
   i *= i;

新的基于范围的 for 循环可用于数组、string 和 STL 容器,还可以用于任何用户定义的数据结构,前提是你的迭代器

  • 在你的自定义数据结构上具有 begin()end() 方法
  • 具有 operator*、operator!= 和 operator++(前缀版本)

可在此 篇文章 中找到一个关于如何编写此类迭代器的示例。

override 和 final

以前,C++ 没有一种方法可以指定派生类中的 virtual 方法覆盖了基类的方法。实际上,派生类中的 virtual 关键字是可选的,这使得情况更加混乱。编写 virtual 方法时可能会出错。例如,让我们看这个示例:

class B 
{
public:
   virtual void f(int) {cout << "B::f" << endl;}
};

class D : public B
{
public:
   virtual void f(float) {cout << "D::f" << endl;}
};

D 中的 f() 方法没有覆盖 B 中同名的方法,因为参数类型不同。以下示例也是错误的,因为 B::fconst,而 D::f 不是,因此它不是覆盖。

class B 
{
public:
   virtual void f(int) const {cout << "B::f " << endl;}
};

class D : public B
{
public:
   virtual void f(int) {cout << "D::f" << endl;}
};

C++11 提供了指定方法旨在覆盖基类 virtual 方法的可能性。这是通过将方法标记为 'override' 来实现的。这不是关键字,而是一个特殊的标识符,可以在其他地方用作标识符。

class B 
{
public:
   virtual void f(int) {cout << "B::f" << endl;}
};

class D : public B
{
public:
   virtual void f(float) override {cout << "D::f" << endl;}
};

上面的代码会产生以下编译器错误:

error C3668: 'D::f' : method with override specifier 'override' 
did not override any base class methods

另一方面,有时你可能不希望某个方法在继承层次结构中被进一步覆盖。可以使用特殊标识符 'final'(不是关键字)来标记函数。以下示例会产生错误。

class B 
{
public:
   virtual void f(int) final {cout << "B::f" << endl;}
};

class D : public B
{
public:
   virtual void f(int) override {cout << "D::f" << endl;}
};
error C3248: 'B::f': 函数被声明为 'final' 不能被 'D::f' 覆盖

Bjarne Stroustrup 在他的 C++11 FAQ 页面上讨论了更多关于 overridefinal 的内容。

C++ 编译器增强:自动向量化器和自动并行化器

到目前为止,C++ 编译器仅使用 CPU 的标量单元。然而,如今,所有 CPU 都拥有向量单元(请参见 维基百科上的 SSE)。这些向量单元包含向量寄存器,可以执行对向量(一系列数字)的操作,只需一条指令。这意味着,如果你有一个不带数据依赖的循环(例如执行求和的循环),它实际上可以使用向量指令执行(当然,在满足某些约束的条件下),从而使执行时间减小数倍。VS11 中的 C++ 编译器已得到增强,可以利用 SIMD 指令并自动向量化它认为安全的循环。此功能默认启用,无需更改你的代码。鉴于程序的特定性,它可以提供显著的隐式性能提升。

除了 SSE 支持外,如今大多数 CPU 也是多核的,这意味着有多个向量单元。因此,除了自动向量化之外,VS11 C++ 编译器还可以进行自动并行化,将计算分散到多个核心上。自动并行化器非常保守,在当前版本中,它只能找到少数可以自动并行化的循环。但是,开发者可以通过使用 <tt>pragma loop(hint_parallel(N))</tt> 来指示编译器自动并行化循环。自动并行化器在 VS11 中默认不启用。你必须转到项目的属性,选择 C/C++ > 代码生成,然后将 "启用并行代码生成" 设置为 "",或者如果你从命令行构建,则使用 /Qpar

Jim Radigan 和 Jim Hogg 在这次 channel9 采访 中深入介绍了自动向量化器和自动并行化器。

IDE 增强功能

C++/CLI IntelliSense

在 VS2010 中,VC++ 团队重新设计了 IntelliSense,但 VS2010 发布时并没有对 C++/CLI 项目提供任何 IntelliSense 支持(这篇 VC++ Team 的博文 解释了原因)。这是一个主要的抱怨,好消息是 VS11 提供了对 C++/CLI 的 IntelliSense 支持。这意味着快速信息、参数帮助、成员列表和自动完成等功能可以与 C++/CLI 正常工作。

C++ 代码片段

代码片段是可重用的代码小块,你可以将其插入到代码中(例如 switchtry-catch 块)。它们已经存在一段时间了,但 C++ 没有。VS11 现在为 switchif-elsefor 循环和其他结构提供了 C++ 代码片段。此外,开发者还可以定义自己的代码片段。Ovidiu Cucu 的这篇 文章 展示了如何操作。

语义着色

IDE 无法对 C++ 代码进行着色的能力一直让我很恼火。很高兴看到 VS11 终于在这方面做了一些事情。新 IDE 默认能够根据类型、enum、宏和其他 C++ 标记进行着色。更多信息请参阅 这里

并行库

Microsoft 在 Visual Studio 2010 中为原生并行编程提供了两个 C++ 库,一个名为并行模式库(或简称 PPL),用于编写任务并行;另一个名为异步代理库,用于使用基于 Actor 的编程模型和消息传递来表达数据流和管道任务。在 VS11 中,这些库得到了扩展,以提供更好的性能、更多的控制以及对最常用并行模式的更丰富支持。

PPL 库随着 Windows 8 和 WinRT 的推出变得更加重要,WinRT 提供了一个新的编程模型,其中所有阻塞操作都是异步的。PPL 使开发者能够更轻松地编写 C++ 异步代码(尽管不如 C# 容易),其中延续(该库的一项新功能)起着关键作用。Artur Laksberg 的这篇 使用 PPL 在 C++ 中进行异步编程 文章讨论了此主题。

VS11 引入了一个新的库(去年夏天宣布),名为 C++ 加速大规模并行(或简称 C++ AMP)。这是一个基于 DirectX 的原生库,允许开发者编写可在异构硬件上运行的并行代码。它旨在让 C++ 开发者轻松进行 GPU 编程。它提供了多维数组、索引、内存传输、分块和数学函数库,同时可以使用语言扩展和编译器限制来控制数据如何在 CPU 和 GPU 之间移动。C++ AMP 的 "Hello world" 应用程序可在此 找到。

在此 博客 上可以找到有关实现这些库的团队的大量信息和示例。

MFC 的一些小更新

VS11 没有对 MFC 进行任何更新,主要是进行了一系列错误修复(包括绘制/绘图问题或内存泄漏),正如 Pat Brenner 在这篇 博文 中所解释的。唯一的重要更改是减小了使用 "MFC 控件" 的静态链接 MFC 应用程序的大小。这些是 Visual Studio 2008 中 MFC 功能包中的控件,前面带有 "MFC" 前缀(例如 CMFCButtonCMFCListCtrlCMFCToolbarCMFCMenuBar 等)。在 Visual Studio 2010 中,使用这些控件的静态链接 MFC 应用程序的大小急剧增加。VS11 解决了这个问题,但由于可能破坏现有代码,因此更改无法回溯到 Visual Studio 2010。解决方案是,如果你不使用这些控件,请在你的应用程序中定义 _AFX_NO_MFC_CONTROLS_IN_DIALOGS。在这种情况下,你的应用程序将链接到一个新的、更小的库版本,它具有相同的方法,但实现不同。如果你在任何时候需要添加 MFC 控件,只需删除该宏,无需其他代码更改。当然,在这种情况下,应用程序将链接到常规库实现,并且大小会增加。这篇 博文 提供了问题的详细信息和解决方案。

鉴于公司对 Windows 8、WinRT 和 Metro 风格应用程序的推动,MFC 的投入不足并不令人意外。不幸的是,这很可能也是未来所有版本的状况。我认为 MFC 除了错误修复和偶尔的头文件更新外,将不再受益于任何增强功能。

Windows 8

Windows 8 是该公司的旗舰产品,VS11 为构建新的 Windows 运行时(称为 WinRT)的 C++ 应用程序提供了强大的支持。(这也是 MFC 没有重要更新的原因。)WinRT 仍然是一个原生运行时。为 WinRT 构建的应用程序称为 Metro 风格应用程序。其优点是相同的 API 对 C++、C#/VB.NET 和 JavaScript 都是可见的。

Microsoft 构建了一个 C++ 扩展,称为 C++ 组件扩展,或简称 C++/CX。它是用于为 WinRT 编写原生组件或应用程序的语言。它具有与 C++/CLI 非常相似的语法,尽管存在一些差异,并且与 C++/CLI(目标是 CLR)不同,C++/CX 目标是 WinRT 和原生代码。有关此主题的更多信息,请阅读 Nish Sivakumar 的 Visual C++ 和 WinRT/Metro - 一些基础知识

在 VS11 中,可以

  • 使用新的原生 XAML 框架或 DirectX 以 C++ 构建 Metro 风格应用程序;实际上,可以在同一个应用程序中使用两者,它们协同工作效率很高。
  • 使用 C++、HTML5/JabaScript 或 .NET 构建 Metro 风格应用程序的 WinRT 组件。

当你在 Windows 8 上安装 VS11 时,你会得到一些额外的 Windows Metro 风格模板。这些模板在其他系统上不可用,因为 WinRT 在之前的操作系统上不存在。

这是一个使用原生 XAML 框架进行 UI 的经典 "Hello world!" C++ 应用程序。基本上没有任何 C++ 代码(除了向导为该项目类型生成的代码)。顶部有一个滑块,一块显示 "Hello world" 的文本。文本块的字体大小与滑块的值绑定。一切都在 XAML 中完成。

<Page
    x:Class="HelloWorldApp.BlankPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:HelloWorldApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{StaticResource ApplicationPageBackgroundBrush}">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="15*"/>
        </Grid.RowDefinitions>
        <Slider Name="slider" Minimum="20" Maximum="80"
                Value="50"
                Grid.Row="0" />
        <TextBlock Text="Hello world!"
                   FontSize="{Binding ElementName=slider, Path=Value, Mode=OneWay}" 
                   HorizontalAlignment="Center"
                   Grid.Row="1" />
    </Grid>
</Page>

结论

VS11 为原生开发者带来了显著的改进或新功能。这些包括 C++11 的新功能(尽管 RTM 后预计会有更多功能)、VC++ 库的更新、VC++ 编译器的改进、C++ IDE 的新功能、对构建 Windows 8 Metro 风格应用程序的支持等。另一方面,MFC 的改进非常少,而且非常重要的一点是,不再支持 Windows XP 和 Windows Server 2003。如果你的原生应用程序仍然以这些操作系统为目标,你将无法使用新的工具集来构建它们。

历史

  • 2012 年 4 月 5 日:初始版本
VS11 原生开发新功能 - CodeProject - 代码之家
© . All rights reserved.