Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МУ_ЛР_ТП_1.doc
Скачиваний:
1
Добавлен:
01.04.2025
Размер:
1.9 Mб
Скачать

6. Оформление отчета

Отчет должен содержать:

  • цель работы и индивидуальное задание;

  • перечень электронной документации, доступной из среды Visual Studio 2010;

  • проверенные варианты решения проблемы deprecated-функций;

  • ошибки в программах и выдаваемые при этом сообщения;

  • описание методов (methods) одного из классов, полученное из MSDN;

  • примеры окон Solution Explorer и Class View.

7. Контрольные вопросы

7.1. Что такое SDK? Приведите примеры использования.

7.2. Перечислите окна, которые можно использовать в среде Visual Studio 2010?

7.3. Объясните назначение команд системы меню сред разработки Visual Studio 2010 и Visual Studio Team System 2008.

7.4. Перечислите основные глобальные и локальные акселераторы сред разработки?

7.5. Какая поддержка документов и приложений Microsoft Office предусмотрена?

7.6. Как изменяется меню главного окна после создания проекта, после добавления файлов в проект, после запуска отладчика?

7.7. Как программируется форматный консольный вывод в языке C#?

7.8. Какие операции и методы класса string вы знаете?

7.9. Какие виды параметров имеются у методов в языке C#?

7.10. Как реализуется сокрытие текста методов и/или классов в окне редактора?

БИБЛИОГРАФИЧЕСКИЙ СПИСОК

1. Пауэрс Л. Microsoft Visual Studio 2008 / Л. Пауэрс, М. Снелл: Пер. с англ. – СПб.: БХВ-Петербург, 2009. – 1200 с.

2. Макки А. Введение в .NET 4.0 и Visual Studio 2010 для профессионалов: Пер. с англ. – М.: ООО "И.Д. Вильямс", 2010. – 416 с.

3. Нэш Т. C# 2010: ускоренный курс для профессионалов: Пер. с англ. – М.: ООО "И.Д. Вильямс", 2010. – 592 с.

4. Онлайн-учебник по C++. - URL: http://en.wikiversity.org/wiki/Introduction_to_C%2B%2B. . Дата последнего обращения: 1.02.13.

5. Онлайн-учебник по C++. - URL: http://cplusplus.com/doc/tutorial/. Дата последнего обращения: 1.02.13.

6. Онлайн-учебник по С/C++. - URL:

http://cplus.about.com/od/learning1/Learn_about_C_and_how_to_write_Programs_in_It.htm. Дата последнего обращения: 1.02.13.

Лабораторная работа № 2

Объектно-ориентированное программирование на управляемом и неуправляемом С++ в интегрированной среде Visual Studio 2010

1. Цель и задачи работы

Ознакомление с технологией создания и отладки объектно-ориентированных программ на управляемом (managed) и неуправляемом (unmanaged) языке С++ в интегрированной среде разработки приложений Visual Studio 2010.

2. Теоретические сведения

Язык С++ является гибридным языком, позволяющим при создании программ использовать как процедурное, так и объектно-ориентиро­ванное программирование (ООП). Известно, что одним из недостатков процедурных языков программирования является ограниченность их предопределенных типов данных. Например, если в программе необхо­димо выполнять сложение матриц, пользователь создает процедуру, например, с именем add, и будет обращаться к ней всякий раз, когда необходимо просуммировать массивы, например:

call add(A,B,C,size);

Решение той же задачи с использованием ООП заключается в создании нового (абстрактного) типа данных, например, matrix, для которого будет переопределен оператор сложения. В этом случае за­пись С=А+В решает задачу сложения матриц, причем используется форма записи, наиболее близкая к математической. В этой записи А,В и С - объекты типа (класса) matrix. Таким образом, создавая классы для конкретной предметной области, программист может адап­тировать язык программирования под задачи этой предметной облас­ти.

Для иллюстрации перехода от процедурного к объектно-ориенти­рованному программированию рассмотрим процедурный и объектно-ори­ентированный варианты реализации такой структуры данных, как стек. Обычно стек позволяет выполнять операции push, pop, top, empty и full. Оператор push помещает значение в стек. Оператор pop считывает и удаляет значение из вершины стека. Оператор top считывает верхнее значение стека. Оператор empty проверяет, пуст ли стек. Оператор full проверяет, полон ли стек.

Процедурная реализация стека

Для реализации стека в С++ средствами процедурного програм­мирования используется структура struct (рис.1). Различные опера­ции со стеком выполняются посредством функций, каждая из которых имеет список параметров, включающий параметр - указатель на стек. Это позволяет изменять стек, избегая его копирования, что выпол­нялось бы в случае передачи параметров по значению (в дальнейшем будет разработана специальная функция, выполняющая передачу пара­метра-стека по значению).

const int max_len=1000;

enum { EMPTY=-1, FULL=max_len-1 };

using namespace std;

struct stack{

char s[max_len];

int top;

};

// Набор стандартных операций со стеком

void reset(stack* stk) { stk->top=EMPTY; }

void push(char c,stack* stk) { stk->s[++stk->top]=c; }

char pop(stack* stk) { return (stk->s[stk->top--]); }

char top(stack* stk) { return (stk->s[stk->top]); }

bool empty(const stack* stk){return (bool)(stk->top==EMPTY);}

bool full(const stack* stk) {return (bool)(stk->top==FULL); }

Рис.1. Файл Stack1.h

Объявление struct в Stack1.h в С++ рассматриваются как объявления нового типа данных, в данном слу­чае типа stack.

На рис.2 приведен пример программы, использующей процедурную реализацию стека. Функции reset, push, pop, top, empty и full представляют собой интерфейс, обеспечивающий все возможности ра­боты со стеком. Однако здесь не реализована инкапсуляция (сокры­тие) данных, т.е. пользователь может изменить содержимое стека, если обратится в обход функций непосредственно к массиву s струк­туры s. Это показано в Stack1.cpp с помощью операторов, которым предшествуют символы комментария.

#include <iostream>

#include "Stack1.h" //Реализация стека

int main()

{

stack s;

char str[40] = {"Characters for stack"};

int i=0;

cout<<str<<endl; // печать строки

reset(&s);

while(str[i])

if(!full(&s))

push(str[i++],&s);

while(!empty(&s))

cout<<pop(&s); // печать стека

cout<<endl;

// s.s[10]='\0'; // нежелательный доступ

// cout<<s.s<<endl;

}

Рис.2. Программа Stack1.cpp

Объектно-ориентированная реализация стека

Объектно-ориентированное программирование на языке С++ осно­вано на расширении возможностей struct. Описание структуры в С++ в общем случае имеет три раздела, каждый из которых может содер­жать поля данных и функции для доступа к этим данным. Эти три раздела называются: закрытый (private), защищенный (protected) и открытый (public). Функции, объявленные внутри struct, имеют не­ограниченный доступ к элементам (полям и функциям) структуры. Лю­бые другие функции (например, main) имеют доступ только к элемен­там открытого раздела struct. На рис.3,4 показана реализация сте­ка, использующая инкапсуляцию данных.

const int max_len=1000;

struct stack{

private:

char s[max_len];

int top;

enum { EMPTY=-1, FULL=max_len-1 };

public:

void reset() { top=EMPTY; }

void push(char c) { s[++top]=c; }

char pop() { return (s[top--]); }

char Top() { return (s[top]); }

bool empty() { return (bool)(top==EMPTY); }

bool full() { return (bool)(top==FULL); }

};

Рис.3. Файл Stack2.h

Struct stack имеет private часть, которая содержит описание данных, и public часть, которая содержит функции-члены, выполняю­щие операции со стеком. Удобно считать private часть используемой только разработчиком, а public часть - спецификацией интерфейса, которую может использовать клиент - пользователь стека. Позже разработчик может менять private часть, не влияя при этом на пра­вильность использования клиентом стекового типа.

#include <iostream>

#include "Stack2.h"

using namespace std;

int main()

{

stack s;

char str[40] = {"Characters for stack"};

int i=0;

cout<<str<<endl;

s.reset(); //s.top=EMPTY; будет неверно

while (str[i])

if (!s.full())

s.push(str[i++]);

while(!s.empty())

cout<<s.pop();

cout<<endl;

}

Рис.4. Программа Stack2.cpp

Отметим, что при выполнении оператора

stack s;

функции main порождает объект s класса stack, содержащий поля данных s[max_len] и top. В общем случае можно породить несколько таких объектов, например, два, если использовать оператор

stack s1,s2;

Поэтому при использовании функций интерфейса необходимо явно указывать объект, к которому выполняется доступ, например:

s1.reset();

Объект, указанный в префиксе обращения к функции, передается в функцию как неявный параметр this - указатель на объект, доступ к которому выполняет функция интерфейса. В данном случае this = &s1. Следовательно, фактическая реализация функции reset будет следующей:

void reset() { this->top=EMPTY; )

Классы в С++ обычно представляются ключевым словом class. Они являются формой struct, у которой спецификация доступа по умолчанию private. Напомним, что у struct доступ по умолчанию - public. Таким образом, struct и class могут использоваться пооче­редно с соответствующей спецификацией доступа.

Объектно-ориентированная реализация строк

В С++ отсутствует встроенный строчный тип. Простейшие опера­ции над строками - это инициализация, конкатенация (соединение) и печать строк. Строки представляются как указатели на char. В таком представле­нии конец строки обозначается символом '\0'. Однако большинство базовых манипуляций со строками используют длины строк. Когда длина строки известна, эффективность строковых операций значи­тельно выше.

На рис. 5, 6 представлен строковый абстрактный тип данных, хранящий длину строки в виде private. Определение класса String хранится в файлах Str.h и Str.cpp. Для каждой строки с помощью new выделяется необходимый объем динамической памяти. Для управ­ления основным представлением строковых указателей применяются библиотечные функции из string.h.

#include <iostream>

#include <string.h>

using namespace std;

class String

{

private:

char* s;

int len;

public:

// Конструктор по умолчанию

String() { s=new char[1]; s[0]=0; len=0; }

String(const String& str); // конструктор копии

String(const char* p) // конструктор инициализации

{ len=strlen(p); s=new char[len+1];

strcpy(s,p); }

~String() { delete [] s; } // деструктор

void assign(const String& str);

void print() const { cout<<s<<endl; }

friend void concat( String& a,const String& b, const String& c);

};

Рис.5. Описание класса String

Определение класса String содержит объявления элементов-дан­ных и элементов-функций. Функциональный элемент, имеющий то же имя, что и класс, носит название конструктора. Конструктор ис­пользуется для создания и инициализации объектов типа String, или для преобразования значений других типов в тип класса. Создание объектов класса часто производится путем распределения свободной памяти посредством оператора new. При удалении таких объектов не­обходимо своевременно освобождать занимаемую ими память, т.е. уничтожать значение типа класса (обычно посредством оператора de­lete). Эту работу выполняет деструктор класса. Деструктор - это функциональный элемент класса, имя которого - то же, что и имя класса, c предшествующим ему символом ~ (тильда).

#include "string.h"

#include "Str.h"

String::String(const String& str){

len=str.len; s=new char[len+1];

strcpy(s,str.s);

}

void String::assign(const String& str){

if(this == &str) return;

else delete [] s; // удаление старой строки

len=str.len;

s=new char[len+1];

strcpy(s,str.s);

}

void concat(String& a,const String& b, const String& c){

a.len=b.len+c.len;

delete [] a.s;

a.s=new char[a.len+1];

strcpy(a.s,b.s);

strcat(a.s,c.s);

}

Рис.6. Определение функций класса String

Конструктор, не требующий параметров, называется конструкто­ром по умолчанию. Это может быть конструктор с пустым списком па­раметров или конструктор, в котором все параметры имеют значения по умолчанию. Конструктор по умолчанию специально предназначен для инициализации массивов объектов класса.

В описании класса String представлены два вида функций (за исключением конструкторов) – функции, являющиеся членами класса (member-функции) и дружественные классу функции (friend-функции), которым разрешен доступ к приватным элементам (данным и member-функциям) класса. В member-функцию передается неявный аргумент через this-указатель и явные аргументы, которые указываются в круглых скобках при обращении к функции. В отличие от member-функции friend-функция не использует this-указатель и все аргументы в нее передаются явно.

На рис. 7 представлена программа Strprog.cpp, использующая абстрактный тип String. Здесь преднамеренно использован ряд объ­явлений для того, чтобы показать, как могут вызываться различные конструкторы. Строковые переменные (объекты) b, d, e используют конструктор по умолчанию.

#include <iostream>

#include "Str.h"

int main()

{

char* str = "Characters for string";

String a(str),b,c("First string \n"),d,e;

b.assign("Second string \n");

concat(d,a,b);

concat(e,d,c);

e.print();

String f=a;

f.print();

String g(c);

g.print();

}

Рис.7. Программа Strprog.cpp

Третий конструктор порождает объекты a и c, используя для инициализации объектов класса String данные типа char*. Этот конструктор также вызывается неявно для преобразования аргумента функции assign из типа char* в тип String.

Работа функции назначения assign основывается на "семантике глубокого копирования", использующего физическое копирование строки (всего объекта) посредством strcpy(). Альтернативным вариантом является "поверхностное копирование", при котором строка не дублируется, а вместо этого добавляется ссылка на единственный экземпляр строки.

Конструктор копии - это конструктор, первым аргументом кото­рого является ссылка на объект того типа, в котором этот конс­труктор объявлен. Конструктор копии в классе X не обязательно должен иметь единственный аргумент типа X&; допускается наличие одного или нескольких дополнительных аргументов, а так же аргу­ментов по умолчанию.

Конструктор копии используется для того, чтобы выполнить ко­пирование одного значения строки в другое, когда строка

  • инициализируется другой строкой ( переменные f и g в прог­рамме Strprog.cpp);

  • передается в виде параметра в функцию (копирование фактичес­ких аргументов - объектов в локальные при передаче по значению);

  • возвращается в виде значения функции.

Конструктор копии настолько важен, что компилятор генерирует его в том случае, если он явно не был напи­сан программистом. Разумеется, сгенерированный компилятором конс­труктор может создать только точную копию исходного объекта, что требуется далеко не всегда. Например, для класса String мы полу­чим копию указателя, и в результате оба объекта будут указывать на один и тот же массив символов в памяти. В этом случае деструк­тор будет дважды освобождать одну и ту же память, что обычно при­водит к ошибке.

Изменим класс stack так, чтобы размер стека задавался при создании объектов класса stack. Для этого поле

char s[max_len];

заменим на два поля:

char *s;

int max_len;

и добавим следующий конструктор класса stack:

stack(int size) { s= new char[size];

max_len=size; top = EMPTY; }

Констуктор копии для стека символов с динамически изменяемым размером будет следующим:

stack::stack(const stack& str)

{

s = new char[str.max_len];

max_len = str.max_len;

top = str.top;

memcpy(s,str.s,max_len);

}

Аналогично можно записать конструктор инициализации и конструктор по умолчанию. Рассмотрим пример использования конструктора копии при пере­даче аргумента типа stack в функцию по значению. Допустим, необ­ходимо исследовать стек и посчитать число вхождений определенного символа. Для этого можно выталкивать элементы из стека, проверяя каждый элемент, до тех пор, пока стек не будет пуст. Естественно, исследовать надо копию стека, которая будет получена с помощью конструктора копии при вызове следующей функции:

int cnt_char (char c, stack s)

{

int count=0;

while(!s.empty()) count+=(c==s.pop());

return (count);

}

Список инициализации конструктора. Статические данные и функции

Если среди элементов - данных класса имеются константы, то инициализировать их можно только в конструкторе, причем для этой цели используется список инициализации конструктора (задается в конструкторе после двоеточия):

class C1{

private:

int var;

const a;

public:

C1(int v, int c): a(c)

{ var = v ; }

};

Отметим, что список инициализации конструктора, использова­ние которого обязательно для констант, ссылок и данных абстракт­ных типов (объектов), может быть использован и для "обычных" элементов клас­са, например:

class C1{

private:

int var;

const a;

public:

C1(int v, int c): a(c), var(v) {}

};

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

stack::stack(const stack& str) : max_len(str.max_len), top(str.top)

{

s = new char[str.max_len];

memcpy(s,str.s,max_len);

}

Язык C++ позволяет объявлять элементы класса (данные и функ­ции) как статические. Для любого класса может быть создана только одна копия статического элемента данных, причем память под стати­ческие элементы данных резервируется при запуске программы до создания объектов класса. Статическое поле данных не может быть инициализировано при описании класса или внутри функции. Следую­щий пример иллюстрирует использование статических элементов дан­ных:

class s{

static sm;

public:

void show() { cout<<"static: "<<sm<<endl; }

};

int s::sm=10;

void main()

{

// int s::sm=5; // ошибка

s s1;

s1.show();

}

Объявление функционального элемента класса как статического означает, что к такой функции можно обратиться до того, как в программе будет создан первый объект класса. Статические функции используются не только для доступа к статическим элементам данных класса, но и для переопределения операций выделения и освобожде­ния динамической памяти.

На рис.8 приведена программа Fstatic.cpp, иллюстрирующая возможные варианты синтаксиса обращений к статическим функцио­нальным элементам класса.

#include <iostream>

using namespace std;

class C1{

private:

int numb;

static int stat;

public:

C1(int i) { numb = i; }

static void func (int i, C1* ptr = 0)

{

if(ptr)

ptr->numb=i;

else

stat = i;

}

static void show_s(void)

{

cout<<"stat="<<stat<<endl;

}

};

int C1::stat = 5;

void main()

{

C1::show_s();

C1::func(10);

C1 obj(15);

obj.show_s();

C1::func(20,&obj);

obj.func(18,&obj);

obj.show_s();

}

Рис.8. Программа Fstatic.cpp

Управляемые и неуправляемые типы языка С++ платформы .NET

Управляемый (контролируемый) код – понятие, введенное разработчиками платформы .NET. Компилятор любого языка, поддерживаемого платформой .NET, создает байт-код (управляемый код) программы, который с одной стороны еще не привязан ни к какой аппаратной платформе, а с другой стороны не содержит в себе никаких особенностей, определяемых языком программирования. Для текстового (визуального) представления байт-кода разработан язык IL (Intermediate Language, промежуточный язык Microsoft), своеобразный ассемблер .NET байт-кода.

Выполнение байт-кода на конкретной аппаратной платформе производится посредством одновременного преобразования его в код конкретного процессора с помощью виртуальной машины .NET, входящей в состав общеязыковой среды исполнения (Common Language Runtime, CLR). Именно виртуальная машина, называемая также JIT-компилятором (JIT - Just-in-Time, в процессе исполнения), работающая в среде CLR, гарантирует безопасное и контролируемое выполнение программы.

Язык С++ - единственный язык платформы .NET, компилятор которого позволяет создавать как управляемый (managed) код для его последующего выполнения в среде CLR, так и неуправляемый (unmanaged) код, являющийся машинным кодом конкретного процессора. Естественно, что неуправляемый код выполняется быстрее байт-кода, но он менее безопасен (разрешается работать с указателями), не является мобильным, при выполнении не контролируется средой CLR.

Объекты управляемого кода всегда создаются в динамической памяти (точнее, в управляемой динамически распределяемой области памяти, называемой кучей). Такие управляемые объекты создаются в программе с помощью оператора new, а удаляются специальной программой среды CLR, называемой сборщиком мусора GC (garbage collection, сборка мусора). В программе на С++ могут одновременно создаваться как управляемые объекты (классы таких объектов объявляются с ключевым словом __gc), так и неуправляемые объекты, для классов которых используется объявление __nogc.

Поскольку в С++ класс и тип являются эквивалентными понятиями, то очевидными становятся такие понятия, как управляемые и неуправляемые типы. Управляемый тип – тип, для которого сборка мусора осуществляется автоматически и для освобождения ресурсов, используемых объектами этого типа, не требуется использовать оператор delete. Вместо того чтобы явно удалять управляемый объект, можно либо сделать так, чтобы на него не указывал ни один указатель, либо явно приравнять этот указатель нулю.

На рис. 9 приведена простейшая управляемая программа на языке С++. При создании проекта управляемой программы необходимо указывать тип программы - CLR. Кроме этого, необходимо с помощью директивы using подключать библиотеку mscorlib.dll, которая делает доступными для компилятора стандартные типы (такие, как Console и Object).

#using <mscorlib.dll> //required by managed C++ code

void main(void)

{

System::Console::WriteLine("Hello World");

}

Рис.9. Программа HelloWorld.cpp

Программа может иметь и управляемые и неуправляемые части кода (рис. 10).

#using <mscorlib.dll>

#include <stdio.h>

// Директива компилятору на генерацию неуправляемого кода.

#pragma unmanaged

// Эта функция будет неуправляемой, она будет транслирована

// в чистый машинный код.

void UnmanagedFunction()

{

printf("Hello, World, from UnmanadegFunction!\n");

}

// Директива компилятору генерировать управляемый код.

#pragma managed

// Это управляемая функция, она будет транслирована в набор инструкций

// .NET байт-кода.

void ManagedFunction()

{

System::Console::WriteLine(S"Hello, World, from ManagedFunction!");

UnmanagedFunction();

}

// Эта функция также будет управляемой.

void main()

{

ManagedFunction();

}

Рис.10. Программа Some.cpp

Класс String платформы .NET

Объект класса System::String инкапсулирует (скрывает внутри объектной оболочки или обертки) строку символов Unicode (типа __wchar_t *). Тип String представляет собой конечный (sealed) класс; это означает, что он не может быть базовым для другого класса. Сам класс String – производный от класса System::Object, являющегося основой иерархии классов платформы .NET. Объект класса String является неизменяемым (константным), т.е. будучи однажды инициализированным, он не может быть изменен. Класс String содержит методы, которые можно использовать для изменения объекта, такие как Insert, Replace, PadLeft. Однако эти методы только возвращают измененный объект, не изменяя сам объект, для которого они вызываются. В отличие от String методы класса StringBuilder позволяют изменять сам объект.

В программе на рис. 11 показано, что метод Replace не влияет на содержимое исходного объекта класса String, но изменяет содержимое объекта класса StringBuilder.

#using <mscorlib.dll>

using namespace System; //for Console and String

using namespace System::Text; //for StringBuilder

void main(void)

{

Console::WriteLine("String is immutable:");

String *ps1 = S"Hello World";

String *ps2 = ps1->Replace('H', 'J');

Console::WriteLine(ps1);

Console::WriteLine(ps2);

Console::WriteLine("StringBuilder can be modified:");

StringBuilder *psb1 = new StringBuilder(S"Hello World");

StringBuilder *psb2 = psb1->Replace('H', 'J');

Console::WriteLine(psb1);

Console::WriteLine(psb2);

char *ps3 = "ASCII string literal"; // неуправляемый объект

__wchar_t *ps4 = L"Unicode string literal"; // неуправляемый объект

String *ps5 = S"String object literal"; // управляемый объект

Console::WriteLine(ps3);

Console::WriteLine(ps4);

Console::WriteLine(ps5);

}

Рис.11. Программа StringReplace.cpp

В программе StringReplace используются строковые литералы (константы), определенные с префиксом S и без него. Строковый литерал, определенный с использованием только кавычек, является указателем на char (типом char*), т.е. указателем на последовательность символов ASCII, заканчивающуюся нулем. А строковый литерал, определенный с префиксом S, является указателем на управляемый объект класса String. Префикс L обозначает строку символов Unicode, имеющую тип __wchar_t *, которая также не является объектом класса String. Отметим, что

  • операционные системы MS Windows 9x работают с ASCII (с однобайтовой кодировкой) и не поддерживают Unicode – двухбайтовую кодировку символов;

  • существует технология компонентных объектов на базе платформы Win32 API (технология COM – Component Object Model), в которой строковые объекты инкапсулируют как строки ASCII, так и строки Unicode; тип этих объектов – BSTR (Basic STRing);

  • существуют библиотеки различных сред разработки (например, библиотеки VCL, MFC), в которых имеются классы строковых объектов, обычно инкапсулирующие строки ASCII.

При работе со строковыми объектами необходимо различать операции сравнения указателей на объекты и операции сравнения самих объектов. В классе String метод Equals используется для сравнения содержания строковых объектов, заданных указателями (поскольку Equals является методом, т.е. member-функцией, сравниваемые объекты задаются this-указателем и указателем, переданным в Equals через явный аргумент). Операция == для указателей будет проверять только равенство адресов в указателях.

В программе на рис. 12 представлены различные варианты сравнения строковых объектов и указателей на объекты.

#using <mscorlib.dll>

using namespace System;

void main(void)

{

String *pstr1;

String *pstr2;

//comparing literal String objects

pstr1 = S"hello";

pstr2 = S"hello";

if (pstr1->Equals(pstr2))

Console::WriteLine("equal"); //executed

else

Console::WriteLine("not equal"); //not executed

if (pstr1==pstr2)

Console::WriteLine("equal"); // executed

else

Console::WriteLine("not equal"); //not executed

//comparing new String objects (non literal)

pstr1 = new String("hello");

pstr2 = new String("hello");

if (pstr1->Equals(pstr2))

Console::WriteLine("equal"); // executed

else

Console::WriteLine("not equal"); //not executed

if (pstr1==pstr2)

Console::WriteLine("equal"); //not executed

else

Console::WriteLine("not equal"); //executed

}

Рис.12. Программа StringLiteral.cpp

Управляемые строковые литералы String и неуправляемые строковые литералы ASCII и Unicode (благодаря автоматической упаковке) можно использовать в выражениях, в которых ожидается использование управляемого строкового объекта String. Однако управляемый строковый объект String нельзя использовать там, где ожидается появление переменных неуправляемых типов.

В программе на рис. 13 представлены примеры допустимых и недопустимых преобразований типов для аргументов функций.

#using <mscorlib.dll>

using namespace System;

#include <wchar.h> //for wchar_t

void ExpectingManagedString(String *str){}

void ExpectingASCIIString(char *str){}

void ExpectingUnicodeString(wchar_t *str){}

void main(void)

{

//expecting a managed type

ExpectingManagedString(S"hello"); // совпадение типов

ExpectingManagedString("hello"); // автоматическое преобразование

ExpectingManagedString(L"hello"); // автоматическое преобразование

//expecting an unmanaged type

ExpectingASCIIString("hello"); // совпадение типов

ExpectingASCIIString(S"hello"); // ошибка!

ExpectingUnicodeString(L"hello"); // совпадение типов

ExpectingUnicodeString(S"hello"); // ошибка!

}

Рис.13. Программа MixingStringTypes.cpp

Класс Array платформы .NET

Обычные массивы являются простым типом указателя, например, int* (или, соответственно, int[]). Управляемые массивы являются управляемыми объектами, расположенными в динамически распределяемой области памяти. System::Array – абстрактный класс, являющийся базовым для всех управляемых массивов. Для определения управляемого массива следует использовать либо ключевое слово __gc, либо указывать, что элементы массива относятся к управляемому типу.

В программе на рис. 14 представлены различные способы объявления массивов. Отметим, что неуправляемые данные можно создавать как в стековой, так и в динамически распределяемой области памяти. Обратите внимание на две закомментированные строки в программе, в которых при объявлении массива задается его размер. Размер массива при объявлении можно задавать только для неуправляемого массива, который располагается в стеке, но нельзя задавать для управляемого массива, поскольку он располагается в динамической памяти и эта память при объявлении еще не выделяется (размер будет указан при выделении памяти с помощью оператора new).

#using <mscorlib.dll>

using namespace System;

// уничтожить предупреждение о переменной, на которую нет ссылки

#pragma warning(disable : 4101)

void main(void)

{

// синтаксис традиционного неуправляемого массива

int *pintUnanagedArrayOnHeap = new int [5];

int intUnManagedArray[5]; //нет ошибки для unmanaged array

// синтаксис управляемого массива,используется __gc

int intManagedArrayOnHeap __gc[] = new int __gc[5];

//int intManagedArray __gc[5]; //ошибка для managed array

// синтаксис управляемого массива, используется

// управляемый тип элементов

String *strManagedArrayOnHeap[] = new String*[5];

//String *strManagedArray[5]; //ошибка для managed array

}

Рис.14. Программа ArraySyntax.cpp

Управляемые массивы наследуют от класса System::Array методы Copy, GetLength, GetType и от класса System::Object методы ToString и Eguals. Управляемые массивы, в отличие от неуправляемых (неконтролируемых) являются безопасными. При попытке доступа к элементу управляемого массива производится проверка принадлежности к диапазону индексов, т.е. контроль границ. При обращении по недопустимому индексу возникает исключение IndexOutOfRangeException.

В программе на рис. 15 показано, как можно использовать обработчик исключений при попытке доступа к несуществующему элементу управляемого массива. Обратите внимание, что массив содержит пять элементов, а в цикле производится попытка установить значение шестого. Программа в обычном С++ выполнила бы такое действие, изменив содержимое памяти за пределами массива.

#using <mscorlib.dll>

using namespace System;

void main ()

{

int intArray __gc[]= new int __gc[5];

for (int i=0; i<6; i++) // неправильная граница цикла!!!

{

try

{

intArray[i] = i;

}

catch (IndexOutOfRangeException *piore)

{

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

Console::WriteLine("Oooops!");

Console::WriteLine(piore->get_Message());

}

}

}

Рис.15. Программа IndexOutOfRangeException.cpp

Как и для неуправляемых массивов, нумерация элементов в управляемых массивах начинается с нуля. Однако, в отличие от неуправляемых массивов, элементы управляемого массива автоматически инициализируются значением, принятым по умолчанию для каждого типа элемента массива. Переменным примитивных типов, таких, как int, char, float, double присваивается нуль. Элементам, указывающим на управляемые объекты, также по умолчанию присваивается нулевое значение (т.е. нулевой указатель). Элементы значимых типов (например, структур) инициализируются с помощью их принятого по умолчанию конструктора (т.е. конструктора, не имеющего аргументов для инициализации).

Программа на рис. 16 иллюстрирует работу с массивами. В ней сравниваются двумерные массивы – управляемый и неуправляемый. Отметим, что синтаксис [ , ] предполагает истинно прямоугольный массив, в то время как в синтаксисе [][] каждый подмассив может иметь разный размер (т.н. массив с неровным правым краем). Назначение закомментированных строк будет приведено в следующем подразделе.

#using <mscorlib.dll>

using namespace System;

void main ()

{

//managed 1D array of int (using __gc)

Console::WriteLine("managed 1D array of int");

int intArray __gc[]= new int __gc[5];

// array<int>^ intArray = gcnew array<int>(5);

for (int i=0; i<intArray->Length; i++)

{

intArray[i] = i;

Console::Write(intArray[i]);

Console::Write("\t");

}

Console::WriteLine();

//managed 2D array of Strings (using managed type)

Console::WriteLine("managed 2D array of Strings");

String *str2DArray[,] = new String *[2,3];

// array<String^,2>^ str2DArray = gcnew array<String^,2>(2,3);

for(int row=0; row<str2DArray->GetLength(0); row++)

{

for(int col=0; col<str2DArray->GetLength(1); col++)

{

str2DArray[row,col] = (row*10 + col).ToString();

Console::Write(str2DArray[row,col]);

Console::Write("\t");

}

Console::WriteLine();

}

//unmanaged 2D array of int (for comparison)

Console::WriteLine("unmanaged 2D array of int");

int int2DArray[2][3];

for(int row=0; row<2; row++)

{

for(int col=0; col<3; col++)

{

int2DArray[row][col] = row*10 + col;

Console::Write(int2DArray[row][col]);

Console::Write("\t");

}

Console::WriteLine();

}

}

Рис.16. Программа Array1.cpp

В программе Array1 управляемый прямоугольный двумерный массив содержит эленты типа String*, а неуправляемый – элементы типа int. Однако управляемый массив может также содержать и элементы неуправляемых типов, между тем как неуправляемый массив – лишь элементы неуправляемых типов.

В программе на рис. 17 сравнивается использование массива массивов (синтаксис [][]) и прямоугольного двумерного массива (синтаксис [ , ]). Для более корректного сравнения оба массива содержат элементы типа int.

#using <mscorlib.dll>

using namespace System;

void main(void)

{

Console::WriteLine("Rectangular array using [,]");

int rect2DArray [,] = new int __gc [3,4]; //managed

// array<int,2>^ rect2DArray = gcnew array<int,2>(3,4);

for(int row=0; row< rect2DArray ->GetLength(0); row++)

{

for(int col=0; col< rect2DArray->GetLength(1); col++)

{

rect2DArray [row,col] = row*10 + col;

Console::Write(rect2DArray[row,col]);

Console::Write("\t");

}

Console::WriteLine();

}

Console::WriteLine("Array of arrays using [][]");

int arrayOfArray[3][4]; //unmanaged

for(int row=0; row<3 ; row++)

{

for(int col=0; col<4; col++)

{

arrayOfArray[row][col] = row*10 + col;

Console::Write(arrayOfArray[row][col]);

Console::Write("\t");

}

Console::WriteLine();

}

}

Рис.17. Программа Array2.cpp

Язык Visual C++ 2005

Язык Visual С++ фирмы Microsoft основывается на трех стандартах: ISO C 95, ISO C++ 98 и Ecma C++/CLI 05.

Управляемый (Managed) Visual С++, входящий в состав Visual Studio 2003 (MC++ 2003 в дальнейшем) значительно изменился в новой редакции, которая входит в Visual Studio 2005, т.е. в MC++ 2005. Поэтому в проекте приложения, использующего старую версию управляемого языка С++, необходимо использовать опцию компилятора /clr:oldSyntax. Опция /clr определяет использование новой редакции языка С++.

Синтаксис языка MC++ 2005 соответствует спецификации C++/CLI (Common Language Infrastructure, Общеязыковая Инфраструктура). Он заменяет старый синтаксис языка MC++ 2003, известный как Managed Extensions for C++.

Наиболее полная информация об этих языках представлена в документации MSDN. Для обращения к ней необходимо выбрать опцию Contents в меню Help (или нажать кнопку Contents), установить в окне Contents фильтр Visual C++, а затем выбрать следующую последовательность опций: Development Tools and Languages -> Visual Studio -> Visual C++ -> Getting Started -> How Do I in Visual C++ -> C++ Language -> Managed Extensions for C++ Syntax Upgrade Checklist (или опции Development Tools and Languages -> Visual Studio -> Visual C++ -> Getting Started -> Reference -> C/C++ Language -> C++ Language Reference -> New C++ Language Features). В окне просмотра документации целесообразно установить фильтр для языков программирования: Language Filter -> C++.

В С++ 2005 появились ключевые слова, состоящие из двух слов, разделенных пробелом, например ref class (класс, используемый в библиотеках DLL). Некоторые из новых возможностей пришли в MC++ 2005 из языка C#, который в области объектно-ориентированного программирования имеет значительно больше возможностей, а также из библиотеки STL (Standard Template Library) языка С++, имеющей богатые возможности по использованию шаблонов классов и функций.

На рис. 18 приведен пример программы на языке MC++ 2005, в которой функция Test1() создает и инициализирует управляемый массив целых чисел, а затем этот массив возвращается как значение функции Test1().

// Test1.cpp

// compile with: /clr

using namespace System;

#define ARRAY_SIZE 10

// Returns a managed array of Int32.

array<Int32>^ Test1() {

int i;

array< Int32 >^ local = gcnew array< Int32 >(ARRAY_SIZE);

for (i = 0 ; i < ARRAY_SIZE ; i++)

local[i] = i + 10;

return local;

}

int main() {

int i;

// Declares an array of value types and initializes with a function.

array< Int32 >^ IntArray;

IntArray = Test1();

for (i = 0 ; i < ARRAY_SIZE ; i++)

Console::WriteLine("IntArray[{0}] = {1}", i, IntArray[i]);

Console::WriteLine();

}

Рис.18. Пример программа на языке MC++ 2005

Для того, чтобы программы Array1.cpp и Array2.cpp перевести с синтаксиса MC++ 2003 на синтаксис MC++ 2005, достаточно заменить строки объявления массивов с использованием __gc на строки с ключевым словом array (на рис. 16 и 17 эти строки закомментированы).

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]