для каких целей был реализован язык managed c
Для каких целей был реализован язык managed c
public ref class CBookManagedCode // Managed C++ класс
<
public:
CBookManagedCode();
public:
int CalculatePrice(interior_ptr nBookID)
<
. // C++ код
>
>;
Чтобы использовать C# классы в обычном C++ проекте
Есть старый большой проект написанный на обычном C++.
Нужно в этом C++ проекте использовать C# классы (в C# работа с файлами быстрее, в C# удобнее работать с базой данных, в C# есть очень много классов списков и т.д.)
Так вот чтобы использовать в обычном C++ проекте классы C# нужно писать промежуточный код (Managed С++).
Напишем новое приложение на C++
1) Создадим новое C++ console application.
2) В свойствах проекта установим значение
Common Language Runtime Support = Common Language Runtime Support (/clr)
#include «stdafx.h» // подключаем С++ библиотеку
#include // подключаем С++ библиотеку
using namespace System; // подключаем С# библиотеку для использования C# класса String
using namespace System::IO; // подключаем С# библиотеку для использования C# класса FileInfo
int _tmain( int argc, _TCHAR* argv[])
<
TCHAR strTitle[] = _T( «d://1.txt» ); // C++ код
String^ strFilename = gcnew String(strTitle); // managed c++ код создает String (класс C#)
// получаем информацию о файле используя C# класс FileInfo
FileInfo^ finfo = gcnew FileInfo(strFilename); // managed c++ код создает FileInfo (класс C#)
// выводим на экран используем std::cout (функция C++)
std::cout «File size: » Length «\n» ;
return 0;
>
// Managed C++
String^ strFilename = gcnew String();
// C#
String strFilename = new String();
В Managed C++ для обозначения ссылок на объекты используется ^
В C# по умолчанию ссылка на объект (поэтому не используется никакого значка)
В Managed C++ для выделения памяти используется gcnew
В C# для выделения памяти используется new
Обзор языков программирования, предоставляемых Visual Studio 2013
Цель лекции
Ознакомление с языками C#, VB, Managed C++, JavaScript, F# и другими, предоставляемыми средой Visual Studio, и их новыми возможностями, реализованными в среде Visual Studio 2013.
7.1. Введение. Набор языков программирования, поддерживаемых в Visual Studio 2013
7.2. Язык C# и его новые возможности в VS 2013.
Если кратко охарактеризовать наиболее популярные языки C++, Java и C# (названные в порядке их появления), то получится примерно следующее.
Разумеется, это мое личное мнение.
Приведем еще один пример, использующий параметризованные типы ( generics ):
Вывод данного примера:
Однако интересно заметить следующее: если попытаться извлечь тип-конкретизацию из объекта g типа Generic напрямую:
Интересно, что я пропускал аналогичный пример в версии Visual Studio 2010 Ultimate. В ней компилятор C# выдал сообщение об ошибке : метод GetType неприменим в данном контексте. Налицо явный прогресс в Visual Studio 2013, по сравнению с предыдущей версией: теперь стало возможно извлекать тип-конкретизацию из объекта, что вполне естественно, с точки зрения предполагаемой программистом семантики данной конструкции.
Хотя официально Microsoft не сообщает о нововведениях в язык C# и его реализацию в версии Visual Studio 2013, проанализированное служит весомым подтверждением того, что команда C# активно работает над улучшением языка и реализации.
BestProg
Managed (управляемые) и unmanaged (неуправляемые) классы в Visual C++. Обзор особенностей работы. Примеры реализации managed и unmanaged классов
Содержание
Поиск на других ресурсах:
1. Какие виды классов существуют в Visual C++? Какие особенности использования разных видов классов?
В системе Visual C++ используются два вида классов:
Работа с указателями на класс подробно описывается в темах:
2. Пример использования класса, который описывает массив из n чисел
Ниже наводятся примеры реализации классов двух видов:
По данным примерам можно разрабатывать собственные классы.
2.1. Unmanaged (неуправляемый) класс
2.1.1. Работа с методами класса с помощью объекта класса
В классе описываются внутренние переменные:
В классе определены следующие конструкторы:
В классе реализованы следующие методы:
Демонстрация использования класса в другом программном коде
2.1.2. Работа с методами класса с помощью указателя ( * ) на класс
2.2. Пример реализации managed (управляемого) класса
Класс содержит 2 конструктора:
Методы класса следующие:
Реализация класса помещена в нескольких модулях (файлах):
Текст модуля MyArray.h следующий:
Текст модуля MyArray.cpp следующий:
2.2.1. Работа с методами класса с помощью объекта класса
Чтобы использовать класс CMyArray в других модулях (файлах) нужно подключить модуль MyArray.h
Объясним некоторые фрагменты кода. При объявлении объекта класса
где число 10 означает размерность массива.
2.2.2. Работа с методами класса с помощью указателя ( ^ ) на класс
В приведенном выше коде в строке
вызывается конструктор CMyArray() с одним параметром (параметризированный конструктор). В конструкторе осуществляется выделение памяти для массива и заполнение элементов массива значениями.
Управляемый C++
Автор: Игорь Ткачёв
Источник: RSDN Magazine #0
Опубликовано: 05.02.2002
Исправлено: 13.03.2005
Версия текста: 1
Итак, “Управляемый C++”. Давайте попробуем вместе прояснить ситуацию и разобраться, насколько он управляем, что под этим понимается и в чём необходимость его появления.
Среди основных задач подобных сред исполнения программ можно отметить следующие:
В свою очередь, C++ разрабатывался как универсальный язык, в том числе и для решения задач реального времени, низкоуровневого программирования и написания драйверов аппаратных устройств, в связи с чем применение в нём автоматической сборки мусора, неявной инициализации переменных и проверки допустимости значений аргументов является неоправданными из-за потери производительности.
Что касается контроля типов, то вот что об этом говорит создатель C++ Бьерн Страуструп:
“Механизм контроля доступа в C++ обеспечивает защиту от случайности, а не от преднамеренного обмана. Любой язык программирования, который поддерживает прямой доступ к памяти, обязательно оставляет любой элемент данных открытым для сознательного “жульничества”, нарушающего явные правила типов, сформулированные для этого элемента”
Прямая работа с операционной системой и различными API, а также “мирное сосуществование” с другими языками являются одной из основных причин успеха C++.
Таким образом, налицо явные противоречия между концепциями управляемой среды и такого языка программирования, как C++. Как же программистам из Редмонда удалось разрешить подобный конфликт и подружить CLR с “неуправляемым” C++?
К счастью, C++ остался самим собой. Мы по-прежнему можем не инициализировать указатели, выходить за границы массивов, забывать освобождать выделенную память, и никакие CLR не могут нам в этом помешать. С другой стороны, C++ должен уметь работать с объектами CLR и позволять этим объектам работать со своим кодом, т.е. играть по правилам той среды, в которой он используется.
Managed Extensions for C++ позволяет C++ программам использовать следующие объекты CLR:
Управляемые типы, массивы и указатели
Как было отмечено выше, CLR поддерживает автоматическую сборку мусора и обеспечивает безопасную передачу данных между различными частями системы. Для обеспечения этих возможностей среда CLR должна владеть полной информацией о типах данных. Теперь мы имеем возможность создавать и использовать такие типы наряду с обычными типами C++, не прибегая к ухищрениям наподобие библиотек типов для COM-объектов.
Идентификатор __gc применяется для объявления сложных типов, массивов и указателей, размещаемых в куче среды исполнения CLR. Сокращение gc, скорее всего, происходит от garbage collection. Рассмотрим пример:
Ещё одной особенностью приведённого выше кода является то, что мы можем создать экземпляр класса только в управляемой куче, например, следующий код является некорректным и приведёт к ошибке компиляции:
__gc arrays
Так же, как и для объектов управляемых типов, память для управляемых массивов выделяется в куче CLR. Массивы могут содержать переменное число элементов и всегда инициализируются при создании. Для объявления управляемых массивов используется специальный синтаксис:
Создать массив объектов Foo можно следующим образом
Как вы можете заметить, в примере с объектом Foo мы не использовали ключевого слова __gc. В этом нет необходимости, так как компилятор уже знает, что имеет дело с управляемым типом.
Для возвращения массивов из функций и для объявления многомерных массивов в MC++ также применяется новый причудливый синтаксис:
Из последнего примера явно следует, что управляемые массивы не являются привычными для нас массивами C++. Скорее, это объекты со своим интерфейсом, и, к сожалению, мы не можем применять для них обычные в C++ методы работы с массивами. Это легко проверить на следующем примере:
__pin
Pin-указатели позволяют зафиксировать объект в памяти и сделать его неперемещаемым до тех пор, пока такой указатель существует как объект и его значение не равно нулю. Рассмотрим пример:
__nogc
Как нетрудно догадаться, это ключевое слово обозначает обычный неуправляемый тип C++. Этот модификатор используется компилятором по умолчанию и приведён здесь лишь для полноты картины.
__value
Согласитесь, размещать абсолютно все переменные в управляемой куче расточительно, особенно если это просто байт или целое, использующиеся в качестве счётчика. Как известно, размещение переменных в стеке является наиболее эффективным для простых переменных с коротким жизненным циклом. Идентификатор __value позволяет объявлять управляемые типы, которые, в отличие от gc-типов, могут размещаться как в управляемой куче, так и в стеке программы. В таблице 1 приведено соответствие между примитивными типами C++ и управляемыми типами CLR.
| C++ | CLR |
|---|---|
| char | Sbyte |
| signed char | Sbyte |
| short | Int16 |
| int | Int32 |
| long | Int32 |
| __int64 | Int64 |
| unsigned char | Byte |
| unsigned short | UInt16 |
| unsigned int | UInt32 |
| unsigned long | UInt32 |
| unsigned __int64 | UInt64 |
| float | Single |
| double | Double |
| void | Void |
С помощью модификатора __value можно объявлять как классы и структуры, так и управляемые перечислимые типы. Более того, это единственный способ объявлять перечисления, которые будет понимать CLR. Например:
Всё правильно, последняя строчка не содержит ошибки. CLR поддерживает типизированные перечисления, поэтому и в MC++ вполне допустимо задавать для них тип.
__box
Ключевое слово __box создаёт обёртку для value-типов, после чего их можно использовать так же, как и gc-классы. Такие языки, как C# и VB.NET, создают обёртки для value-типов автоматически, в MC++ неявное преобразование запрещено из соображений производительности.
__gc pointers
Интерфейсы
Как известно, CLR не поддерживает множественного наследования классов. Возможно, это и правильно. Во-первых, не все языки его реализуют, а, во-вторых, механизм виртуальных базовых классов, обычно использующийся для разрешения конфликтов при множественном наследовании, значительно усложняет структуру таблицы виртуальных методов класса и делает вызовы виртуальных функций крайне неэффективными. С другой стороны, “облегчённый вариант” множественного наследования не порождает таких проблем и широко используется при разработке COM-компонентов вообще и с использованием библиотеки ATL в частности.
В отличие от наследования классов CLR разрешает множественное наследование интерфейсов, но это наследование и сами интерфейсы имеют ряд важных ограничений.
Цель этих ограничений – избежать классических конфликтов при множественном наследовании, включая неоднозначность доступа к данным наследуемых объектов, и свести таблицу виртуальных методов к простому линейному массиву указателей на виртуальные функции, реализация которых будет осуществлена в наследуемых классах.
Ко всему прочему MC++ разрешает двум или более наследуемым gc-интерфейсам иметь методы с идентичными именами и параметрами. Для того чтобы избежать неоднозначности при реализации этих методов можно использовать следующий синтаксис:
Кроме того, MC++ поддерживает для интерфейсов реализацию по умолчанию (default implementations):
Строки
Доступ к управляемым строкам как к обычным неуправляемым символам может быть осуществлён следующим образом:
Делегаты и события
Как следует из примера, один делегат может использоваться для обслуживания нескольких функций, т.е. делегат это не просто указатель, а список указателей. Ещё один момент – для TestDelegate2 мы опустили указание объекта, это допустимо, поскольку этот метод является статическим. Кроме того, с помощью делегатов можно вызывать даже функции Windows API, если они соответствующим образом объявлены:
Вспоминая COM трудно поверить, что это всё, что нужно для реализации механизма рассылки событий. Но не так просто обстоят дела для компилятора, фактически он генерирует примерно следующий код:
Здесь мы видим, что компилятор создаёт уже знакомый нам делегат и генерирует несколько методов, управляющих подпиской и генерацией событий. Чтобы покончить с делегатами, приведём пример, в котором участвуют источник событий и их получатель:
Для подписки на события используется оператор “+=”, для её отмены “-=”.
Свойства
Кроме того, в CLR допустимо объявление индексируемых свойств, для которых справедливо следующее:
Метаданные
Вполне естественно, что значительная часть Managed Extensions for C++ отвечает за управление генерацией метаданных.
Импорт метаданных
Программа на MC++ может импортировать метаданные путём включения директивы #using специфицирующей файл, которым может быть:
Видимость классов
Видимость полей и методов класса
Пользовательские атрибуты
Атрибуты представляют собой универсальное средство расширения метаданных. Любой класс или его элемент может быть помечен атрибутом, информация о котором будет сохранена в метабазе. Практическое применение атрибутов мы уже видели на примере [DllImport]. Этот атрибут говорит управляющей среде, что специфицированную им функцию следует искать в модуле user32.dll. Атрибуты могут использоваться не только самой CLR или компиляторами, доступ к ним возможен из любой программы. Так же мы можем определять и свои собственные атрибуты (Custom Attributes).
Объявление пользовательского атрибута производится следующим образом:
Все пользовательские атрибуты должны быть помечены атрибутом attribute и происходить от класса System::Attribute или его наследников. Забавно, не правда ли? Вот вам ещё одно применение атрибутов – чтобы класс стал атрибутом, нужно его пометить атрибутом attribute. В остальном это просто тип данных, информация о котором так же сохраняется в метабазе. Когда же вы применяете этот атрибут к вашим объявлениям типов, его параметры сохраняются вместе с описанием вашего типа данных. Значение перечисления AttributeTargets позволяет указывать, где синтаксически можно использовать атрибут. Определение этого перечисления выглядит следующим образом:
Применение оператора «ИЛИ» также допускается.
Обработка исключений
фактически являются идентичными.
Управляемые операторы
CLR поддерживает операторы, и в MC++ их объявление допустимо. Но, к сожалению, использовать обычную семантику вызова операторов нельзя из-за принятой в MC++ работы с управляемыми объектами через указатели. Тем не менее, такие языки, как C# и VB.NET, лишены этого недостатка и игнорировать такую возможность не стоит. Нельзя также использовать и ключевое слово operator для объявления операторов в управляемых классах, для этого следует пользоваться предопределёнными в CLR именами. Далее приведено соответствие между операторами и их CLR именами.
| op_Decrement | — |
| op_Increment | ++ |
| op_Negation | ! |
| op_UnaryNegation | — |
| op_UnaryPlus | + |
| op_Addition | + |
| op_Assign | = |
| op_BitwiseAnd | & |
| op_BitwiseOr | | |
| op_Division | / |
| op_Equality | == |
| op_ExclusiveOr | ^ |
| op_GreaterThan | > |
| op_GreaterThanOrEqual | >= |
| op_Inequality | != |
| op_LeftShift | > |
| op_Subtraction | — |
Опции компилятора и препроцессор
Опция компилятора /clr
#pragma unmanaged, #pragma managed
Вполне допустимо использование управляемого и неуправляемого кода в одном модуле. Прагма unmanaged заставляет генерировать компилятор неуправляемый, “родной” для используемой платформы код. Естественно, в таком коде вы не можете использовать управляемые объекты.
_MANAGED
Этот предопределённый макрос устанавливается компилятором в 1, когда используется опция /clr. Интересно, что прагма unmanaged никак не влияет на его значение, т.е. обе следующие функции будут возвращать 1:
Опция компилятора /FAs
Разное
Мы уже достаточно много выяснили об MC++, но есть ещё несколько моментов, о которых следует упомянуть.
__identifier
Это ключевое слово введено в расширение для того, чтобы мы имели возможность использовать любые другие ключевые слова в качестве идентификаторов. В следующем примере мы используем класс с именем “operator”:
__abstract
__abstract говорит компилятору о том, что наш класс или интерфейс является абстрактным и создание экземпляра этого класса не допускается, он может использоваться только как базовый класс.
__sealed
Это ключевое слово запрещает использовать специфицируемый класс в качестве базового класса.
__typeof
Оператор __typeof возвращает объект System::Type, с помощью которого можно получить исчерпывающую информацию о заданном управляемом типе.
Статические конструкторы
Управляемый класс может иметь конструктор, который будет вызван средой CLR только один раз для всех объектов данного класса. Это полезно для инициализации статических переменных класса. Порядок вызова таких конструкторов не гарантируется, но вызов всегда будет сделан до создания первого объекта данного класса.
Атрибут [ParamArray]
В CLR допустимо объявление функций с переменным числом параметров, но реализация этой возможности отличается от стандартного для C++ способа. На самом деле список аргументов передаётся как один параметр, являющийся управляемым массивом и помеченный атрибутом System::ParamArray. На MC++ это объявление выглядит следующим образом:
В C# использование атрибута ParamArray встроено в сам язык, и вместо него используется ключевое слово params :
Вызов нашего метода на C# будет выглядеть следующим образом:
Т.е. фактически компилятор C# преобразует список аргументов в массив и затем передаёт его в функцию. C++ такими способностями не обладает, и нам придётся явно создавать массив, явно его инициализировать и явно передавать в функцию:
Смешанный код
В отличие от других CLR-языков MC++ позволяет легко смешивать управляемый и неуправляемый код. Это представляет определённый интерес, и далее мы проведём серию смелых экспериментов для выяснения механизмов их взаимодействия. Для примера возьмём следующий текст:
Нас будут интересовать различия работы с данными, вызов управляемой функции из неуправляемого кода и наоборот. Препарируем этот текст опцией компилятора /FAs и посмотрим, что у нас получилось:
Первое, на что следует обратить внимание – в одном модуле у нас нормально уживаются MSIL и ассемблер, из чего можно сделать вывод, что MC++ фактически содержит два кодогенератора. В обоих случаях вызов функций производится через специальные заглушки, что вполне понятно, единственный вопрос – это эффективность таких вызовов. Обращение к переменной происходит напрямую в обоих случаях, с той лишь разницей, что каждая функция делает это по-своему. Это лишний раз подтверждает способность CLR работать с памятью напрямую, что не совсем обычно для управляемой среды.
С вызовами всё в порядке.
При обсуждении делегатов мы рассмотрели объявление неуправляемой функции с помощью атрибута [DllImport], но теперь у нас могут возникнуть вполне законные сомнения в необходимости его применения и следующий пример это наглядно демонстрирует:
Макрос в начале примера необходим из-за конфликта имён, возникающего при подключении файла windows.h.
Как и ожидалось, данный код выводит на консоль строчку «123». Интересно то, что это выглядит так же, как и обычная Win32 программа, к тому же она подчиняется тем же правилам. Например, вызов CoInitialize здесь также необходим, как и для любого приложения, являющегося COM-клиентом. Можно опять усомниться в эффективности вызовов между управляемым и неуправляемым кодом, но никто не мешает нам обрамить весь этот текст прагмами unmanaged/managed и сократить накладные расходы до одного вызова неуправляемой функции. Обрамлять в данном случае стоит и саму директиву #import, так как объявление функции в неуправляемой секции заставляет компилятор генерировать для неё неуправляемый код вне зависимости от места её реализации. Например, в следующем примере мы получим ошибку компиляции (как мы знаем, неуправляемый код не может использовать управляемые объекты), хотя сама функция определена в управляемой секции.
Заключение
Ещё один вопрос касается терминологии. Что такое “управляемый” и “неуправляемый” код? С неуправляемым всё ясно – это обычный “родной” код Windows/Intel. С управляемыми объектами тоже понятно – CLR может их полностью контролировать. Не понятно только, как быть с обычными C++ программами, которые не используют управляемые объекты, но компилируются в MSIL код.
Пусть они тоже будут… управляемыми, хотя мы-то с вами точно знаем, что это не так :o)
Кратко и быстро разбираемся с C++ CLI
Так сложилось, что по мере рабочей необходимости мне приходится интенсивно использовать C++/CLI и, соответственно, довольно часто объяснять новичкам, что это, как работает, как использовать и, зачем вообще надо. Так что со временем появилось желание написать статью с обзором языка, ответами на некоторые распространенные вопросы и показать места, где могут удачно лечь грабли.
Что это?
К тому же, в этом синтаксисе не было отличий между указателем на нативный тип и на управляемый (в обоих случаях использовалась «*»), не было ключевого слова для обозначения нулевого указателя и прочее. Это и толкнуло Microsoft на создание новой ревизии языка, о которой и пойдет речь в данной статье.
Замечание: эти две версии синтаксиса называются, как ни странно, «old syntax» и «new syntax», и какую именно использовать можно выбирать в настройках компиляции проекта. Впрочем, при создании новых сборок лучше использовать новый синтаксис, так как старый помечен как устаревший и просто плох.
Зачем нужно?
2) Можно вызывать уже написанный на плюсах код. Действительно, поскольку у нас остались все возможности обычного C++, то можно создавать управляемые обертки для существующих классов на нативных плюсах. Это дает намного большие возможности по вызову неуправляемого кода, нежели PInvoke, который с классами работать не умеет.
Как работает?
Все очень просто. При компиляции кода на С++/СLI получается сборка, содержащая как MSIL код, так и машинные команды, в которые превратились строки, написанные на «чистых» плюсах. «Но как же быть с кроссплатформеностью?» — спросите вы, и будете совершенно правы. Никак. В частности, это означает, что не получится собрать один и тот же бинарник для 32 и 64 битных версий (собрать все под «Any CPU»). Такова расплата за использование всех возможностей С++. Естественно, это относится к тому варианта, когда используется микс из управляемого и неуправляемого кода. Всего есть несколько вариантов компиляции:
• /clr — поддержка управляемого и нативного кода с использованием нового синтаксиса.
• /сlr:pure — нативный код не поддерживается. Однако при этом можно использовать небезопасный ) код, в той мере, как это можно делать, к примеру, в С#-сборках при использовании директивы unsafe.
• /clr:safe — Только управляемый безопасный код. Аналог — компиляция C#-сборки без использования unsafe.
• /clr:oldSyntax — аналог /clr, только используется старый синтаксис.
Как выглядит?
Вот примеры сравнения основных конструкций для С# и C++/CLI.
Объявление класса
public sealed class Class1 : Class2
public ref class Class1 sealed : Class2
Объявление структуры
public struct Class1 : IEquatable
public value class Class1 : IEquatable
Объявление интерфейса
public interface ISomeInterface
public interface class ISomeInterface
Объявление перечисления
Создание управляемого объекта
В C++/CLI для обозначения ссылок на управляемые объекты используется «^» вместо «*». Это очень удобно чтобы различать объекты, которые потом надо удалить и те, которые не надо. Также при создании локального ссылочного объекта можно использовать семантику стека:
Object obj();
Это имеет смысл либо при использовании объектов, реализующих IDisposable (речь об этом пойдет позже) либо для value-типов. Заметим, что в плане хранения и использования value-типов С++/CLI дает большую свободу, чем C#, поскольку программист может сам выбирать — использовать ссылку или значение. Таким образом вполне можно в некоторых ситуация сэкономить на количестве boxing/unboxing операций.
Создание управляемого массива
Неуправляемые массивы при этом создаются как обычно.
Передача параметров в метод
void Method( int byValue, ref int byRef, out int outValue);
void Method( int byValue, int %byRef, [ out ] int %outValue);
Переопределение метода
Реализация шаблона IDisposable
protected virtual void Dispose( bool disposing)
<
if (disposing)
<
//release managed resources
>
//release unmanaged resources
>
Class1()
<
//release managed resources
//Аналог финализатора
!Class1()
<
//release unmanaged resources
>
>
Что осталось за кадром?
Понятно, что в одну статью все поместить не удалось. Не рассмотренными остались такие вопросы как:
• Синтаксис делегатов, свойств, методов расширения, foreach и прочее
• Жонглирование из управляемого в неуправляемый и обратно объектами, массивами и прочим
• Что поддерживается и нет из того, что есть в С# и в обычном C++
• Особености компиляции приложений со сборками С++/CLI
• Вопросы производительности. Когда и в чем можно получить выигрыш? Где можно внезапно потерять?


