VISUAL C++

Исследование новых возможностей C++ и MFC в Visual Studio 2010

Сумит Кумар

Visual Studio 2010 предоставляет огромные возможности для разработчиков на С++ — от использования новых возможностей Windows 7 до улучшенных средств более эффективной работы с большими кодовыми базами.

В этой статье я объясню, как Microsoft решила некоторые значимые проблемы, с которыми сталкивались разработчики на C++. Конкретнее, Visual Studio 2010 поддерживает более современную модель программирования за счет введения основных языковых средств из будущего стандарта C++0x и переработки стандартной библиотеки, чтобы она позволяла использовать новые языковые средства. Появились новые инструменты и библиотеки для создания параллельных программ. Вы также найдете более совершенные IntelliSense и средства распознавания кода, способные работать с большими кодовыми базами. Кроме того, вы получите выигрыш от повышенного быстродействия библиотек и прочей функциональности, применяемых на этапах проектирования, разработки, компилирования и связывания.

В Visual Studio 2010 система сборки перенесена в MSBuild, чтобы повысить ее гибкость и обеспечить поддержку ориентации на разные платформы. А доработанная библиотека MFC позволит задействовать всю мощь новых Windows 7 API и писать отличные приложения для Windows 7.

Давайте подробнее рассмотрим усовершенствования в Visual Studio 2010, ориентированные на C++.

Основные языковые средства C++0x

Принятие нового стандарта C++ уже не за горами. Чтобы помочь вам приступить к работе с расширениями C++0x, компилятор Visual C++ в Visual Studio 2010 поддерживает шесть основных языковых средств C++0x: лямбда-выражения, ключевое слово auto, ссылки rvalue, static_assert, nullptr и decltype.

Лямбда-выражения неявно определяют и конструируют безымянные объекты-функции (function objects). Лямбды предоставляют упрощенный естественный синтаксис для определения объектов-функций там, где они используются, не создавая дополнительных издержек.

Объекты-функции — очень эффективный способ настройки поведения алгоритмов Standard Template Library (STL) и могут инкапсулировать как код, так и данные (в отличие от обычных функций). Но определять объекты-функции было неудобно, потому что для этого приходилось писать целые классы. Более того, они определялись вовсе не в том месте, где использовались, и это отсутствие локальности дополнительно затрудняло их применение. В библиотеках пытались снять остроту части проблем, но это слабо помогало из-за того, что синтаксис становился еще сложнее, а сообщения компилятора об ошибках были весьма невнятными. Кроме того, использование объектов-функций из библиотек менее эффективно, так как объекты-функции, определенные как поля данных, не подставляются в строку.

Лямбда-выражения решают эти проблемы. Следующий фрагмент кода иллюстрирует лямбда-выражение, используемое в программе для удаления целых чисел между значениями переменных x и y из целочисленного вектора:

v.erase(remove_if(v.begin(),
   v.end(), [x, y](int n) { 
   return x < n && n < y; }),
   v.end());

Во второй строке расположено лямбда-выражение. Квадратные скобки, называемые интродукторами лямбды (lambda-introducer), указывают определение лямбда-выражения. В данном случае лямбда принимает целое значение n в качестве параметра, а генерируемый лямбда-выражением объект-функция имя для поля данных:x и y. Сравните эти строки кода с эквивалентным объектом-функцией, написанным вручную, чтобы оценить, насколько удобнее и быстрее писать лямбды:

class LambdaFunctor {
public:
  LambdaFunctor(int a, int b) : m_a(a), m_b(b) { }
  bool operator()(int n) const {
    return m_a < n && n < m_b; }
private:
  int m_a;
  int m_b;
};
v.erase(remove_if(v.begin(), v.end(),
  LambdaFunctor(x, y)), v.end());

Ключевое слово auto всегда присутствовало в C++, но им редко пользовались, потому что оно не давало особой пользы. Смысл этого ключевого слова в C++0x пересмотрен, и теперь оно автоматически определяет тип переменной по ее инициализатору. Auto делает код лаконичнее, редотвращает ошибки с типами и ошибки усечения. Оно также помогает сделать код более обобщенным, позволяя писать шаблоны, где меньше внимания уделяется типам промежуточных выражений. Следующий код показывает, как ключевое слово auto избавляет вас от указания типа шаблона в цикле for, в котором перебирается вектор:

vector<int> v;
for (auto i = v.begin(); i != v.end(); ++i) {
// code 
}

Ссылки rvalue — новый ссылочный тип, введенный в C++0x, который помогает решить проблему необязательного копирования и обеспечивает корректное перенаправление (perfect forwarding). Когда в правой части выражения присваивания находится rvalue, объект в левой части этого выражения может заимствовать ресурсы у объекта в правой части, не выполняя отдельную операцию выделения памяти; тем самым реализуется семантика перемещения.

Корректное перенаправление позволяет написать единственный шаблон функции, принимающий n произвольных аргументов и прозрачно перенаправлять их другой произвольной функции. Природа аргумента (модифицируемый, const, lvalue или rvalue) в процессе перенаправления сохраняется:

template <typename T1, typename T2> void functionA(T1&& t1, T2&& t2) {
  functionB(std::forward<T1>(t1), std::forward<T2>(t2));
}

Подробное описание ссылок rvalue выходит за рамки этой статьи, поэтому дополнительные сведения приведены в документации MSDN по ссылке msdn.microsoft.com/library/dd293668(VS.100).

Static_assert позволяет проверять контрольные выражения (assertions) при компиляции, а не в период выражения. И дает возможность инициировать ошибки компиляции с собственными, более понятными сообщениями. Ключевое слово static_assert особенно полезно для проверки допустимости параметров шаблона. Например, компиляция следующего кода приведет к ошибке «error C2338: custom assert: n should be less than 5»:

template <int n> struct StructA {
  static_assert(n < 5, "custom assert: n should be less than 5");
};

int _tmain(int argc, _TCHAR* argv[]) {
  StructA<4> s1;
  StructA<6> s2;
  return 0;
}

Ключевое слово nullptr добавляет null-указателям безопасность типов в многопоточной среде и тесно связано со ссылками rvalue. В качестве null-указателя часто используются макрос NULL (определенный как 0) и литерал 0. До сих пор это не было проблемой, но они плохо работают в C++0x из-за потенциальных проблем в корректном перенаправлении (perfect forwarding). Поэтому ключевое слово nullptr отчасти было введено для того, чтобы избежать таинственных сбоев в функциях корректного перенаправления.

Nullptr является константой типа type nullptr_t, который можно преобразовать в любой тип-указатель, но не в другие типы вроде int или char. Помимо использования в функциях корректного перенаправления, nullptr можно применять везде, где раньше в качестве null-указателя был задействован макрос NULL.

Но будьте осторожны: NULL по-прежнему поддерживается компилятором и пока не заменен nullptr. Это сделано в основном для того, чтобы не разрушать существующий код, в котором часто используется NULL. Но в будущем nullptr должен полностью заменить NULL, и на данном этапе NULL следует рассматривать только как средство поддержания обратной совместимости.

Наконец, decltype позволяет компилятору логически распознавать тип, возвращаемый функцией, исходя из произвольного выражения, и делать корректное перенаправление более обобщенным. В прошлых версиях для двух произвольных типов T1 и T2 нельзя было логически вычислить тип выражения, в которым эти два типа присутствовали. Так вот, decltype дает вам возможность заявлять, что, например, выражение с аргументами шаблона — такое как sum<T1, T2>() — имеет тип T1+T2.

Изменения в стандартной библиотеке

Значительная часть стандартной библиотеки C++ была переписана, чтобы задействовать преимущества новых языковых средств C++0x и увеличить ее быстродействие. Кроме того, в эту библиотеку внесли много новых алгоритмов.

Стандартная библиотека в полной мере использует ссылки rvalue. Такие типы, как vector и list, теперь имеют собственные конструкторы и операторы присваивания move. Операции повторного выделения памяти под векторы используют семантику move через эти новые конструкторы, поэтому, если в ваших типах есть конструкторы и операторы присваивания move, библиотека будет выбирать их автоматически.

Теперь вы можете создать общий указатель (shared pointer) на объект прямо при конструировании этого объекта с помощью новой функции шаблона make_shared<T> в C++0x:

auto sp = 
  make_shared<map<string,vector>>
  (args);

В Visual Studio 2008 с той же целью вам пришлось бы написать:

shared_ptr<map<string,vector>> 
  sp(new map<string,vector>(args));

Использовать make_shared<T> удобнее (вы будете реже набирать имя типа), надежнее (новое ключевое слово предотвращает классическую утечку памяти с безымянным shared_ptr, так как указатель и объект создаются одновременно) и эффективнее (выполняется одна операция динамического выделения памяти вместо двух).

В библиотеке также содержится новый, более «интеллектуальный» тип указателей — unique_ptr (его появление стало возможным благодаря введению ссылок rvalue). В итоге auto_ptr переведено в разряд устаревших; с unique_ptr вы не столкнетесь с такими подвохами auto_ptr, как возможность перемещения, но не копирования. Это позволяет вам реализовать строгую семантику владения, не затрагивая безопасность. Новое ключевое слово также отлично работает с контейнерами Visual C++ 2010, поддерживающими ссылки rvalue.

В контейнерах появились новые функции-члены cbegin и cend, которые позволяют использовать const_iterator для просмотра содержимого контейнера независимо от его типа:

vector<int> v;
 
for (auto i = v.cbegin(); i != v.cend(); ++i) {
  // i is vector<int>::const_iterator
}

В Visual Studio 2010 в стандартную библиотеку добавлено большинство алгоритмов, предложенных в различных технических документах C++0x. Теперь в ней доступно подмножество библиотеки преобразований Dinkumware, и вы можете очень легко выполнять различные преобразования, например UTF-8 в UTF-16. Стандартная библиотека поддерживает распространение исключения через exception_ptr. Много обновлений было внесено в заголовочный файл <random>. В этом выпуске есть однонаправленный список (singly linked list) с именем forward_list. Появился заголовочный файл <system_error>, улучшающий диагностику. Кроме того, многие средства TR1, которые в предыдущем выпуске находились в пространстве имен std::tr1 (например, shared_ptr и regex), теперь являются частью стандартной библиотеки в пространстве имен std.

Усовершенствования в параллельном программировании

В Visual Studio 2010 введена платформа параллельных вычислений (Parallel Computing Platform), которая помогает быстро писать высокопроизводительный параллельный код и в то же время предотвращает тонкие ошибки в параллельной обработке. Это позволит вам избавиться от некоторых традиционных проблем, связанных с параллельной обработкой.

Parallel Computing Plat­form состоит из четырех основных частей: Concurrency Runtime (ConcRT), Parallel Patterns Library (PPL), Asynchronous Agents Library и средств отладки и профилирования параллельного кода.

ConcRT — самый низкий программный уровень, взаимодействующий непосредственно с ОС, и осуществляющий арбитраж множества параллельных компонентов в их конкуренции за ресурсы. Поскольку это процесс пользовательского режима, он может отзывать ресурсы, когда используются его механизмы кооперативной блокировки. ConcRT поддерживает локальность и предотвращает переключение задач между разными процессорами. Он также использует механизм планирования пользовательского режима (User Mode Scheduling, UMS) из Windows 7, что в ряде случаев может резко повысить производительность, даже если механизм кооперативной блокировки не применяется.

PPL предоставляет шаблоны для написания параллельного кода. Если вычисления допускают разложение на независимые операции, которые можно представить функциями или объектами-функциями, каждая из этих операций может рассматриваться как задача (task). Концепция задач намного ближе к предметной области, чем потоки, которые уводят вас от предметной области, заставляя думать об оборудовании, ОС, критических секциях и т. п. Задача может выполняться параллельно с другими задачами независимо от того, что именно делают эти другие задачи. Например, сортировку двух половин массива можно выполнять как две параллельные задачи.

PPL включает параллельные классы (task_handle, task_group и structured_task_group), параллельные алгоритмы (parallel_invoke, parallel_for и parallel_for_each), параллельные контейнеры (combinable, concurrent_queue и concurrent_vector), а также синхронизирующие примитивы, поддерживаемые ConcRT (critical_section, event и reader_writer_lock), — все они полностью поддерживают концепцию задач. Все компоненты PPL находятся в пространстве имен concurrency.

Группы задач позволяют выполнять наборы задач и ждать завершения их всех. В частности, в примере с сортировкой задачи, обрабатывающие две половины массива, могут быть одной группой задач. Тогда вы получите гарантию, что эти две задачи будут завершены в конце вызова функции-члена wait, как показано ниже на примере рекурсивной сортировки «quicksort», написанной с применением параллельных задач и лямбд:

void quicksort(vector<int>::iterator first,
vector<int>::iterator last) {
  if (last - first < 2) { return; }
  int pivot = *first;
  auto mid1 = partition(first, last, [=](int elem) { 
    return elem < pivot; });
  auto mid2 = partition( mid1, last, [=](int elem) { 
    return elem == pivot; });
  task_group g;
  g.run([=] { quicksort(first, mid1); });
  g.run([=] { quicksort(mid2, last); });
  g.wait();
}

Этот код можно еще больше улучшить, используя structured_task_group, поддерживаемую алгоритмом parallel_invoke. Он принимает от двух до десяти объектов-функций и выполняет их все параллельно, используя столько ядер, сколько предоставляет ConcRT, а затем ждет их завершения:

parallel_invoke(
  [=] { quicksort(first, mid1); },
  [=] { quicksort(mid2, last); } );

parallel_invoke(
  [=] { quicksort(first, mid1); },
  [=] { quicksort(mid2, last); } );

Каждая из задач может порождать несколько подзадач. Сопоставление между задачами и потоками (и обеспечение оптимального использования всех ядер) берет на себя ConcRT. Поэтому разложение ваших вычислений на максимально возможное количество задач поможет задействовать все доступные ядра.

Еще один полезный параллельный алгоритм — parallel_for, применяемый для параллельного перебора в цикле:

parallel_for(first, last, functor);
parallel_for(first, last, step, functor);

Здесь параллельно вызываются объекты-функции с каждым индексом, начиная с первого и заканчивая последним.

Asynchronous Agents Library предоставляет модель программирования, основанную на потоках данных (dataflow-based programming model), где вычисления зависят от доступности необходимых данных. Эта библиотека базируется на концепциях агентов, блоков сообщений и функций передачи сообщений. Агент — это компонент приложения, выполняющий определенные вычисления и асинхронно взаимодействующий с другими агентами для решения более крупной вычислительной операции. Взаимодействие между агентами осуществляется через функции передачи сообщений и блоки сообщений.

Агенты имеют значимый жизненный цикл, в ходе которого они проходят различные стадии. Они не предназначены для тонкого распараллеливания, достигаемого с помощью задач PPL. Агенты опираются на ConcRT-компоненты планирования и управления ресурсами и помогают избежать проблем, возникающих при использовании общей памяти в параллельных приложениях.

Чтобы задействовать преимущества этих шаблонов, вам не нужно связывание с дополнительными компонентами, равно как не требуется и распространять какие-либо редистрибутивы. ConcRT, PPL и Asynchronous Agents Library реализованы в msvcr100.dll, msvcp100.dll и libcmt.lib/libcpmt.lib попутно со стандартной библиотекой. Реализации PPL и Asynchronous Agents Library в основном заключены только в заголовочных файлах.

Отладчик Visual Studio теперь поддерживает ConcRT и позволяет легко отлаживать ошибки, связанные с параллельной обработкой, — в отличие от Visual Studio 2008, где отладчик ничего не знал о высокоуровневых концепциях распараллеливания. В Visual Studio 2010 имеется средство профилирования параллельного кода, с помощью которого можно визуализировать поведение параллельных приложений. В отладчик введены новые окна, визуализирующие состояние всех задач в приложении и их стеки вызовов. Рис. 1 показаны окна Parallel Tasks и Parallel Stacks.

Figure 1 Parallel Stacks and Parallel Tasks Debug Windows
Рис. 1 Отладочные окна Parallel Stacks и Parallel Tasks

IntelliSense и производительность в соотношении разработка-время

В Visual Studio 2010 включена совершенно новая инфраструктура IntelliSense и просмотра. Она не только быстро работает в проектах с большими кодовыми базами, но и предоставляет ряд новых средств, повышающих производительность труда на этапе разработки.

В IntelliSense содержатся такие средства, как активная система отчетов об ошибках и подсказки Quick Info; они базируются на новом клиентском интерфейсе компилятора, который «на лету» разбирает единицы трансляции и предоставляет полную и точную информацию о семантике кода даже в процессе модификации файлов исходного кода.

Все средства просмотра кода, такие как представление классов и иерархия классов, теперь используют информацию об исходном коде, хранящуюся в базе данных SQL, которая обеспечивает индексацию и занимает фиксированный объем памяти. В отличие от предыдущих выпусков Visual Studio 2010 IDE всегда отвечает, и вам больше не придется ждать, пока не будет выполнен повторный разбор единиц компиляции из-за изменений в заголовочном файле.

Активная система отчетов об ошибках в IntelliSense (знакомые красные волнистые линии) выделяет синтаксические и семантические ошибки при просмотре и редактировании кода. Задержав курсор мыши над одной из таких ошибок, вы получите сообщение (рис. 2). Окно со списком ошибок также показывает ошибку из текущего просматриваемого файла, равно как и ошибки, обнаруженные Intelli­Sense в любой части единицы компиляции. Вся эта информация доступна вам без выполнения процесса сборки.

Figure 2 Live Error Reporting Showing IntelliSense Errors
Рис. 2 Активная система отчетов об ошибках в IntelliSense

Кроме того, при наборе директивы #include в раскрывающемся списке показываются релевантные включаемые файлы, и этот список обновляется по мере ввода новых символов.

Новая функция Navigate To (Edit | Navigate To или Ctrl+запятая) ускорит вам поиск файла или символа. Она предоставляет результаты поиска в реальном времени, исходя из уже набранных вами частей строк (рис. 3). Эта функция также работает с файлами C# и Visual Basic и является расширяемой.

Figure 3 Using the Navigate To Feature
Рис. 3 Использование функции Navigate To

Call Hierarchy (вызывается клавишами Ctrl+K, Ctrl+T или из контекстного меню) позволяет перемещаться к любым функциям, вызываемым из конкретной функции, или к конкретной функции, вызываемой из всех интересующих вас функций. Это более совершенная версия функции Call Browser, содержавшейся в предыдущих выпусках Visual Studio. Окно Call Hierarchy организовано намного лучше и показывает вызовы из деревьев для любой функции, присутствующей в окне, а также вызовы к этим деревьям.

Заметьте: хотя все функции просмотра кода доступны как для чистого C++, так и для C++/CLI, IntelliSense-средства вроде активной системы отчетов об ошибках и Quick Info не будут поддерживаться для C++/CLI в финальном выпуске Visual Studio 2010.

В этом выпуске усовершенствованы и другие средства редактора. Так, популярная функция Find All References, применявшаяся для поиска ссылок на элементы кода (классы, члены класса, функции и т. д.) в рамках полного решения, стала более гибкой. Результаты поиска можно еще больше сужать, используя команду Resolve Results из контекстного меню.

Неактивный код теперь сохраняет семантическую информацию, не теряя цветовые выделения (раньше он становился просто серым). На рис. 4 показано, как затемняется неактивный код, но при этом сохраняет все цветовые выделения, относящиеся к семантической информации.

Figure 4 Inactive Code Blocks Retain Colorization
Рис. 4 Неактивные блоки кода сохраняют цветовые выделения

Помимо уже описанных функций, редактор в Visual Studio 2010 просто удобнее в использовании.Новая IDE на базе Windows Presentation Foundation (WPF) основательно переработана, и из нее были убраны все нагромождения в UI. Окна документов, например редактора кода и дизайнера, можно перемещать за пределы основного окна IDE и отображать на нескольких мониторах. Стало намного проще увеличивать или уменьшать содержимое окна редактора кода, используя клавишу Ctrl и колесико мыши. IDE также обеспечивает более широкие возможности расширения.

Системы сборки и проектов

В Visual Studio 2010 внесены существенные изменения в систему сборки (build system) и в систему проектов (project system) для C++.

Самое важное изменение заключается в том, что для сборки проектов на C++ теперь используется MSBuild. MSBuild является расширяемым ядром управления сборкой и базируется на XML.Оно применялось для проектов на C# и Visual Basic в предыдущих версиях Visual Studio. Теперь MSBuild — общая система сборки от Microsoft для всех языков. Ее можно использовать как в сборочной лаборатории, так и на индивидуальных компьютерах разработчиков.

Процессы сборки проектов на C++ теперь определяются в терминах MSBuild (файлы задач и целевых платформ) и дают вам более широкие возможности в настройке, управлении и прозрачности.

Тип файлов проектов на C++ получил новое расширение: .vcxproj. Visual Studio будет автоматически обновлять старые файлы и решения .vcproj до нового формата. Также имеется утилита командной строки vcupgrade.exe, позволяющая обновлять отдельные проекты из командной строки.

В прошлом вы могли использовать только тот набор инструментов (компилятор, библиотеки и т. д.), который предоставлялся текущей версией Visual Studio. И начать работу с новой IDE было невозможно до перехода на новый набор инструментов. Visual Studio 2010 решает эту проблему и позволяет выполнять сборку с ориентацией на несколько разных версий наборов инструментов. Например, работая в Visual Studio 2010, вы могли бы ориентироваться на компилятор и библиотеки Visual C++ 9.0. На рис. 5 показаны соответствующие настройки, доступные на странице свойств.

5 Targeting Multiple Platform Toolsets
Рис. 5 Ориентация на несколько Platform Toolset

Применение MSBuild делает систему сборки кода на C++ гораздо более расширяемой. Если система сборки по умолчанию не удовлетворяет вашим потребностям, вы можете расширить ее, добавив собственный инструмент или введя какую-либо дополнительную стадию сборки. При операциях сборки MSBuild использует задачи как повторно применяемые единицы исполняемого кода. Вы можете создавать собственные задачи и расширять систему сборки, определяя их в XML-файле. MSBuild генерирует задачи по таким XML-файлам «на лету».

Существующие платформы и наборы инструментов можно расширять добавлением файлов .props и .targets в папки ImportBefore и Import­After. Это особенно полезно для поставщиков библиотек и инструментов, которые предназначены для расширения существующих систем сборки. Вы также можете определить собственный набор инструментов платформы (platform toolset). Кроме того, MSBuild предоставляет более полную диагностическую информацию, упрощая отладку проблем сборки и делая сборки с приращением (incremental builds) более надежными. Плюс вы можете создавать системы сборки, теснее связанные с системой контроля версий исходного кода и сборочной лабораторией и менее зависимые от конфигураций компьютеров разработчиков.

Система проектов, размещаемая поверх системы сборки, тоже использует преимущества гибкости и расширяемости MSBuild. Система проектов распознает процессы MSBuild и позволяет Visual Studio прозрачно показывать информацию, доступную от MSBuild.

Все изменения видны на страницах свойств и могут настраиваться в соответствии с вашими потребностями. Вы можете сконфигурировать систему проектов на использование собственной платформы (например, существующих платформ x86 или x64) или собственного отладчика. Страницы свойств позволяют писать и интегрировать компоненты, динамически обновляющие значения свойства в зависимости от контекста. Система проектов Visual Studio 2010 дает возможность даже создать собственный UI для чтения и записи свойств вместо механизма на основе страниц свойств.

Меньшее время компиляции и более высокая производительность

Помимо уже описанных улучшений в использовании функциональности этапа проектирования, Visual Studio 2010 также повышает скорость компиляции, качество и производительность приложений, создаваемых с помощью компилятора Visual C++, в результате множества усовершенствований, внесенных в процесс генерации кода.За счет ряда оптимизаций скорость компиляции на платформах x64 заметно увеличена.

Производительность некоторых приложений зависит от рабочего набора (working set). Размер кода для архитектуры x64 был уменьшен на 3–10% за счет множества оптимизаций в этом выпуске, что привело к повышению скорости работы таких приложений.

Генерация кода с применением SIMD (Single Instruction Multiple Data), важная для разработчиков игр и приложений, имеющих дело с аудио, видео и графикой, также оптимизирована для повышения качества и производительности кода. Улучшения обеспечивают, в частности, разрыв ложных зависимостей, векторизацию инициализаций векторов констант и более эффективного выделения регистров XMM для удаления лишних загрузок, сохранений и перемещений. Кроме того, было оптимизировано семейство внутренних команд __mm_set_**, __mm_setr_** и __mm_set1_**.

Для большей производительности приложения следует компилировать с применением Link Time Code Generation (LTCG) и Profile Guided Optimization (PGO).

За счет ряда оптимизаций скорость компиляции на платформах x64 заметно увеличена. Компиляция с LTCG, рекомендованная для более качественной оптимизации, обычно выполняется дольше, чем без LTCG, особенно в больших приложениях. В Visual Studio 2010 компиляция с LTCG ускорена вплоть до 30%. В этом выпуске введен выделенный поток для записи PDB-файлов, поэтому вы увидите ускорение процесса связывания при использовании ключа /DEBUG.

Проходы с инструментарием PGO выполняются быстрее за счет введения поддержки версий оснащенных двоичных файлов, не содержащих блокировок. Также появился новый вариант POGO — PogoSafeMode, который позволяет указывать, какой режим следует использовать при оптимизации приложения — безопасный или быстрый. По умолчанию применяется быстрый режим. Безопасный режим медленнее быстрого, но безопасен в многопоточной среде.

Повышено качество кода, генерируемого компилятором. Теперь полностью поддерживаются Advanced Vector Extensions (AVX), которые очень важны для приложений, выполняющих интенсивные вычисления с плавающей точкой на процессорах AMD и Intel; они включаются встроенными параметрами и /arch:AVX. Точность вычислений с плавающей точкой повышается при использовании параметра /fp:fast.

Создание приложений для Windows 7

В Windows 7 появился целый ряд новых потрясающих технологий и средств, а также добавлены новые API, и Visual Studio 2010 обеспечивает доступ ко всем новым Windows API. Компоненты Windows SDK, необходимые для написания кода для неуправляемых Windows API, поставляются вместе с Visual Studio 2010. Вы можете задействовать такие инновационные API, как Direct3D 11, DirectWrite, Direct2D и Windows Web Service, используя заголовочные файлы SDK и библиотеки, доступные в Visual Studio 2010.

Этот выпуск Visual Studio не только обеспечивает доступ ко всем Windows API, но и упрощает написание Windows-приложений с помощью кардинально переработанной MFC. К значительной части функциональности Windows 7 можно получить доступ через библиотеки MFC, не обращаясь к неуправляемым API напрямую. Ваши существующие MFC-приложения заиграют «новыми красками» в Windows 7 просто за счет перекомпиляции. Ну а новые приложения смогут задействовать все преимущества новых средств.

MFC теперь обеспечивает улучшенную интеграцию с оболочкой Windows. Интеграция вашего приложения с Windows Explorer может быть более полной за счет применения обработчиков файлов для предварительного просмотра, вывода эскизов и поиска, поддерживаемых в этом выпуске. Эти средства предоставляются как выбираемые параметры в MFC Application Wizard (рис. 6) MFC автоматически сгенерирует проект ATL DLL, реализующий эти обработчики.

Figure 6 MFC Application Wizard with File Handler Options
Рис. 6 MFC Application Wizard со страницей параметров обработчика файлов

Одно из самых заметных изменений UI в Windows 7 — новая панель задач. MFC позволяет быстро задействовать преимущества таких средств, как списки переходов (jump lists), строки ярлычков эскизов (tabbed thumbnails), предварительного просмотра эскизов (thumbnail preview), полосок прогресса (progress bars), оверлейных значков (icon overlay) и т. д. На рис. 7 показаны предварительно просматриваемые эскизы и строка с ярлычками эскизов для MFC-приложения типа MDI.

Figure 7 Tabbed Thumbnail and Thumbnail Preview in an MFC Application
Рис. 7 Предварительный просмотр эскизов и строка с их ярлычками в MFC-приложении

Ленточный UI теперь имеет стиль, принятый для лент в Win­dows 7, и в вашем приложении можно «на лету» менять UI в любой момент во время разработки с нескольких лент в стиле Office на ленту в стиле Windows 7, используя раскрывающийся список стилей рис. 8.

Figure 8 Ribbon-Style Dropdown in an MFC Application
Рис. 8 Раскрывающийся список Style в MFC-приложении

MFC дает возможность вводить в ваши приложения мультисенсорную поддержку (multi-touch) и передает вам соответствующие сообщения для обработки при возникновении различных событий касания. Вам нужно лишь зарегистрироваться на события касания и жестов, и они будет направляться вашему приложению. Кроме того, MFC-приложения по умолчанию поддерживают высокие DPI-разрешения, поэтому они способны адаптироваться к экранам с высоким DPI, и при этом их окна не выглядят размытыми или мозаичными. MFC на внутреннем уровне масштабирует и изменяет шрифты и другие элементы, гарантируя четкость отображения вашего UI на экранах с высоким DPI.

Кроме новых средств Windows 7, существовала и другая функциональность, введенная со времен Windows Vista, но не поддерживавшаяся в предыдущих выпусках MFC.Эта ситуация теперь исправлена. Например, Restart Manager — полезная функция Windows Vista, которая позволяет сохранять приложение перед его принудительным завершением. Приложение может вызвать эту функцию и после перезапуска восстановить свое состояние. Теперь вы можете в полной мере использовать Restart Manager в своем MFC-приложении для более элегантной обработки его краха и перезапуска. Чтобы разрешить перезапуск и восстановление в существующем приложении, просто добавьте строку кода:

CMyApp::CMyApp() {
  m_dwRestartManagerSupportFlags = 
    AFX_RESTART_MANAGER_SUPPORT_RESTART;
// other lines of code ...
}

Новые MFC-приложения получают эту функциональность автоматически при использовании MFC Application Wizard. Механизм автоматического сохранения доступен приложениям, которые сохраняют документы, а пользователь может определять интервалы автоматического сохранения. В MFC Application Wizard для программы можно выбрать поддержку лишь перезапуска или только восстановления (применимо к приложениям типа Doc/View).

Еще одно добавление — диалог Windows Task, который является улучшенным видом окна сообщения (рис. 9). В MFC теперь есть оболочка диалога Task, которую можно применять в приложениях.

Figure 9 Task Dialog
Рис. 9 Диалог Task

MFC Class Wizard возвращается

В библиотеку MFC не только добавлена новая функциональность — этот выпуск упрощает работу с MFC в Visual Studio IDE. Было очень много просьб вернуть MFC Class Wizard (рис. 10), и вот он возвращен и вдобавок усовершенствован. С его помощью можно добавлять классы, обработчики событий и другие элементы приложения.

Figure 10 MFC Class Wizard
Рис. 10 MFC Class Wizard

Другое дополнение — Ribbon Designer (дизайнер лент), который позволяет визуально проектировать ленточный UI (вместо его определения в коде, как в Visual Studio 2008) и сохранять его как XML-ресурс. Этот дизайнер, очевидно, будет полезен при разработке новых приложений, но и в существующих приложениях с его помощью можно обновить UI. XML-определение можно создать простым добавлением временной строки к существующему определению ленточного UI в коде:

m_wndRibbonBar.SaveToXMLFile(L"YourRibbon.mfcribbon-ms");

Полученный XML-файл можно использовать как файл ресурса и в дальнейшем модифицировать с помощью Ribbon Designer.

Заключение

Visual Studio 2010 — важный выпуск в эволюции Visual C++, и он во многих отношениях облегчает жизнь программистам. В этой статье я лишь кратко обрисовал наиболее важные усовершенствования, связанные с C++. Более подробное описание различных средств см. в документации MSDN и в блоге группы Visual C++ по ссылке blogs.msdn.com/vcblog, сообщения в котором послужили основой для некоторых разделов этой статьи.

Сумит Кумар (Sumit Kumar) — менеджер программ в группе Visual C++ IDE. Магистр компьютерных наук (Техасский университет в Далласе).

Благодарю следующих технических специалистов: Стефан Т. Лававей (Stephan T. Lavavej), Мариан Лупару (Marian Luparu) и Тарек Мадкур (Tarek Madkour)