Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Теллес М. - Borland C++ Builder. Библиотека программиста - 1998

.pdf
Скачиваний:
764
Добавлен:
13.08.2013
Размер:
4.35 Mб
Скачать

Borland C++ Builder (+CD). Библиотека программиста 151

ActiveX из компонентов VCL. — Примеч. перев.

Использование управляющих элементов ActiveX

Используются управляющие элементы ActiveX в CBuilder так же, как и любые другие управляющие элементы. Вы выбираете управляющий элемент со страницы палитры компонентов ActiveX (или куда вы его положили) и кладете его на форму. Устанавливаете нужные вам свойства, оставляя неизменными некоторые значения по умолчанию, а остальное управляющий элемент сделает сам. Основная разница между ActiveX и VCL проявляется при установке, когда вам нужно поставлять не только исполняемый файл приложения, а все OCX-файлы для управляющих элементов ActiveX. Кроме того, вам нужно зарегистрировать новые управляющие элементы ActiveX в той системе, куда вы их устанавливаете.

Динамическое создание управляющих элементов

Один из наиболее частых вопросов в отношении использования управляющих элементов ActiveX в CBuilder является вопрос о динамическом создании ссылки на управляющий элемент ActiveX во время работы приложения (вместо использова ния дизайнера форм для добавления в форму ссылки на элемент). Вы можете подумать, что это должно делаться легко, так как CBuilder относится к управляющим элементам ActiveX так же, как к компонентам VCL, и будете правы.

Замечание

Полный исходный код для этого примера можно найти на сопроводительном компакт-диске в каталоге Chapter6\DynActive.

Давайте возьмем простой пример, в котором создается ссылка на объект VCFormulaOne при создании формы. Для этого вам нужно знать несколько вещей. Во-первых, вам нужно изменить заголовочный файл формы, чтобы были подключены нужные заголовочные файлы. В отличие от VCL заголовочные файлы для управляющих элементов ActiveX не подключаются компилятором в ваш проект автоматически. В дополнение к заголовочному файлу вы должны описать указатель на компонент для использования при создании управляющего элемента. Вот модифицированный заголовочный файл (Unit1.h) с изменениям и, выделенными подсветкой:

//——————————————————————————— #ifndef Unit1H

#define Unit1H //——————————————————————————— #include <vcl\Classes.hpp>

#include <vcl\Controls.hpp> #include <vcl\StdCtrls.hpp> #include <vcl\Forms.hpp> #include <vcl\VCFrmla1.hpp> #include <vcl\OleCtrls.hpp>

//——————————————————————————— class TForm1 : public TForm

{

__published: // IDE-managed components ActiveX и CBuilder Professional

private: // User declarations TVCFormulaOne *VCFormulaOne1; public: // User declarations

__fastcall TForm1(TComponent *Owner);

Borland C++ Builder (+CD). Библиотека программиста 152

};

//——————————————————————————— extern TForm1 *Form1; //——————————————————————————— #endif

Как только вся предварительная работа сделана, вам остается только создать сам управляющий элемент. Добавьте обработчик для события формы OnCreate, щелкнув дважды на форме. Добавьте следующий код в метод формы FormCreate:

void __fastcall TForm1::FormCreate(TObject *Sender)

{

VCFormulaOne = new TVCFormulaOne(this); VCFormulaOne->Parent = this; VCFormulaOne->Left = 10; VCFormulaOne->Top = 10; VCFormulaOne->Width = ClientWidth-20; VCFormulaOne->Height = ClientHeight-20; VCFormulaOne->Visible = true;

}

Как вы видите из предыдущего кода, нет разницы между созданием управляюще го элемента ActiveX и компонента любого другого типа. Вы должны создать экземпляр компонента через оператор new и затем присвоить его свойству Parent (родитель) (которое автоматически генерирует CBuilder) форму, на которой этот элемент будет проживать. Как только управляющий элемент создан и свойству Parent присвоено значение, остальные свойства определяются обычным образом. Свойства Left, Top, Width и Height поддерживаются CBuilder автоматически при генерации «обертки».

Конкретный пример: броузер HTML

Теперь, когда мы знаем все, что можно знать о работе с управляющими элементами ActiveX (то есть знаем, что они то же самое, что и обычные компоненты), пора использовать их для написания какого-нибудь реального примера для демонстра ции мощи ActiveX и Internet-компонентов

CBuilder.

В этом примере мы построим броузер HTML-страниц, который можно использовать для просмотра HTML-файлов на вашей локальной станции. Это полезно, так как будущее файлов помощи Windows лежит именно в HTML (согласно Microsoft). Поэтому этот броузер можно расценивать как замену системы помощи Windows. Так что это мощный пример, не правда ли?

Замечание

Полный исходный код для этого примера можно найти на сопроводительном компакт-диске в каталоге Chapter6\HTMLViewer.

На рис. 6.3 представлена форма, с которой мы будем работать в этом проекте. Форма содержит комбинированный список, управляющий элемент HTML (со страницы Internet палитры компонентов), поле ввода, кнопку и поле статического текста (метку). Это все, что нужно для решения задачи.

Процесс, который мы будем использовать, довольно прост. Когда пользователь введет URL (адрес в Internet) в поле ввода и нажмет кнопку Поехали!, мы будем пытаться загрузить HTML-документ.

Borland C++ Builder (+CD). Библиотека программиста 153

Если введенный локатор ресурсов (URL) не похож на адрес в Web, мы будем считать, что это имя локального файла на диске. Чтобы так делать, вы должны кое-что понимать о том, как управляющий элемент HTML работает с файлами.

Рис. 6.3. Форма броузера HTML

Если к управляющему элементу HTML попадает правильный локатор ресурсов, он проверяет, существует ли в данный момент подключение к Internet. Если нет, то пытается произойти подключение, настроенное на этого пользователя, отображая стандартное окно диалога подключения с вводом пароля. Если подключение к Internet есть, управляющий элемент использует интерфейс Internet-соединений (который является частью программного интерфейса Windows 32 API) для создания соединения. В этот момент HTML-файл находится и загружается в броузер.

Более интересный случай, в нашем аспекте, когда файл локален, то есть находится на жестком диске пользователя (или другом механизме хранения данных). Объект HTML, поставленный с CBuilder, содержит механизм запроса документов от сервера (RequestDoc), но, похоже, не имеет прямых способов загрузки данных с локального диска. Внешность, однако, обманчива. Метод RequestDoc управляюще го элемента HTML умеет на самом деле загружать локальные файлы, если вы знаете, как это сделать. Слава Богу, мы знаем, как это делается.

Добавьте обработчик события для нажатия на кнопку Go! на форме и следующий код в него:

void __fastcall TForm1::Button1Click(TObject *Sender)

{

AnsiString s = Edit1->Text;

if ( strstr(Edit1->Text.c_str(),»//») == NULL) s = «File:///» + s;

HTML1->RequestDoc(s); ComboBox1->Items->Add( s );

}

В предыдущем фрагменте мы сначала получаем текст из поля ввода. Считая, что что-нибудь там есть, мы ищем строку // в этом тексте. Если она там найдена, то это означает, что строка содержит

Borland C++ Builder (+CD). Библиотека программиста 154

действительный http-адрес в формате http://xxx для Internet-адресов. Если же эта строка не найдена, мы предполагаем, что пользователь хотел посмотреть локальный файл. В этом случае мы используем специальный модификатор устройства file: для доступа к локальной файловой системе. Когда управляющий элемент HTML находит модификатор file:, то он относится к оставшейся части адреса как к локальному пути файла (или сетевому пути файла) и пытается открыть этот файл. Чтобы прочитать файл HTML1.HTM в каталоге c:\temp, например, вам нужно использовать строку file://c:\temp\html1.htm. Выглядит смешно, но работает.

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

void __fastcall TForm1::ComboBox1Change(TObject *Sender)

{

AnsiString s = ComboBox1->Text; HTML1->RequestDoc(s); Edit1->Text = s;

}

Этот метод просто берет строку из поля ввода комбинированного списка и передает ее в управляющий элемент HTML. Таким образом, запрошенный файл загрузится в броузер и будет отображен. И наконец, содержимое поля ввода будет изменено на текущую строку текста, позволяя пользователю при необходимости отредактировать адрес.

На рис. 6.4 показано окно запущенного приложения, в котором загружен HTML-файл, поставляемый вместе с управляющими элементами VC в каталоге CBuilder\OCX\Vci.

Рис. 6.4. Программа броузера HTML в действии

На этом мы дописали программу управления HTML, которая показывает, как легко и быстро вы можете использовать HTML-компонент в ваших приложениях. Использование ActiveX очень просто в CBuilder, возможно, даже проще, чем в средах разработки, которые умеют создавать

Borland C++ Builder (+CD). Библиотека программиста 155

управляющие элементы ActiveX на месте.

Что мы узнали в этой главе?

В этой главе вы увидели, как просто и быстро добавить компоненты ActiveX в среду CBuilder и в ваши приложения CBuilder. Технология ActiveX довольно широко распространена в данный момент в мире пользователей Windows, так что простота, с которой CBuilder обращается с этими управляющими элементами, делает использование этих многосторонних компонентов даже еще более приятным.

Вот основные моменты этой главы:

·Мы научились добавлять новый управляющий элемент ActiveX в палитру компонентов и делать его частью системы.

·Мы выяснили, какие файлы генерирует CBuilder при добавлении нового управляющего элемента

ActiveX в систему.

·Мы научились создавать в нашей форме CBuilder ссылку на управляющий элемент ActiveX динамически.

·Мы научились использовать компонент HTML, поставляемый с версией системы CBuilder Professional, для создания простого, но мощного броузера HTML с запоминанием адресов и автоматической загрузкой.

Одно последнее замечание относительно управляющих элементов ActiveX, поставляемых с CBuilder. Когда система была запущена в продажу, управляющие элементы ActiveX поставлялись только в версии Professional и выше1. В конце мая 1997 года компания Borland сделала Internet- компоненты доступными на ее Web-сайте. Если у вас версия системы CBuilder Trial Edition или Beginning Edition и вы хотите использовать эти управляющие элементы, зайдите на Web-сайт компании Borland (http://www.borland.com)2 и посмотрите, доступны ли они.

1Client/Server Suite. — Примеч. перев.

2Web-адрес представительства компании Borland в России http://www.borland.ru. — Примеч. перев.

Глава 8. Работа с базами данных

Внутреннее устройство баз данных

Динамическое создание баз данных

Обработка событий в базах данных

Проверка корректности данных

Просмотр данных

Работа с несколькими таблицами

Одна из наиболее важных особенностей системы CBuilder — встроенная в нее возможность (и соответствующие условия) работы с базами данных. Для начала программист на С++ получит

возможность простейшего доступа к базам данных за счет предоставляемых стандартных компонентов, которые в CBuilder поддержива ют большое количество разнообразных форматов. Стандартные форматы баз данных фирмы Borland, поддерживаемые напрямую Borland Database Engine (демон поддержки баз данных фирмы Borland), включают в себя dBase, Paradox и InterBase. Кроме того, последняя версия CBuilder содержит драйверы, которые позволяют осуществлять

Borland C++ Builder (+CD). Библиотека программиста 156

Connectivity) может быть использована с CBuilder, что предоставляет вам доступ практически к любой базе данных, существующих в мире программ под Windows.

В CBuilder возможность работы с базами данных это нечто гораздо большее, чем просто поддержка большого количества форматов баз данных. Кроме этого, обширная VCL системы CBuilder выполняет почти всю важную работу по соединению с базой данных, поддержке данных в базе и обеспечению всех остальных моментов, связанных с лежащими в основе баз данных таблицами. Как мы увидим в этой главе, вы можете создать полноценную программу просмотра баз данных, позволяющую добавлять, удалять, изменять и просматривать записи данных в базе, не написав для этого ни строки кода. Попробуйте-ка такое в другой среде разработки (Delphi, конечно, не в счет). Если бы это было все, на что способна VCL, она уже сама по себе была бы отличной системой. Но, более того, CBuilder предоставляет полный контроль над функционированием баз данных, вплоть до уровня записей.

В этой главе мы сделаем чуть больше, чем пробежка по верхам объектов баз данных в CBuilder. Кроме изучения присоединения к базам данных ODBC, мы рассмотрим внутреннее устройство баз данных, создав Проводник полей баз данных, который позволит вам просмотреть поля и их типы в выбранной вами базе данных. Мы рассмотрим создание таблиц баз данных «на ходу», в соответствии с запросами пользователя. Мы даже создадим программу просмотра баз данных (что мы анонсировали выше), который позволит загружать произвольные записи баз данных в сетку просмотра (grid). Это предоставит пользователю возможность по своему усмотрению выбирать базу данных, выводить на экран поля, которые его интересуют , и даже отфильтровывать записи, которые его не интересуют. Все это реализовано буквально в нескольких строках, написанных программистом (в данном случае, мной). Короче говоря, эта глава очень насыщена материалом, который вы наверняка захотите применить в ваших собственных приложениях.

Разбираемся во внутреннем устройстве баз данных

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

То, что находится в базе данных кроме самих данных, называется схемой (schema). Схема представляет собой описание для каждого поля, которое образовывает столбец в базе данных. Схема описывает такие вещи, как данные о полях, об индексах и об общем количестве полей в каждой таблице базы. В большинстве классов C++ прослеживается тенденция игнорировать эту сторону проблемы, фокусируя свое внимание вместо этого (что понятно) на собственно данных в базе. Время от времени, тем не менее, вам будет нужно просмотреть нестандартные базы. Хорошим примером могут служить приложения, занимающиеся импортом данных, распространением баз данных или отображением баз данных из WWW. Все эти типы приложений должны уметь загружать базы данных любого выбранного пользователем типа, не зная при этом ничего о полях данных, на основе которых реализованы эти базы.

В качестве первого примера мы создадим программу для просмотра полей баз данных. Эта программа не будет выводить никаких данных из базы, определенной пользователем, но вместо этого будет отображать схему базы на уровне полей.

Я покажу вам, как изучить поля в базе данных, определив не только название полей, но также типы и размер полей. Владея такой информацией, написание в CBuilder генеральной программы

Borland C++ Builder (+CD). Библиотека программиста 157

просмотра и изменения данных в базе вне зависимости от ее структуры полей будет почти что тривиальной задачей. С какой стати CBuilder будет располагать всей информацией о полях и структуре базы? На самом деле все системы управления базами данных нуждаются в информации схемы. CBuilder она нужна для поддержки полей, воспринимающих данные (data-aware fields), например, для класса DBGrid. Если информация уже получена, почему бы не сделать ее доступной для использования и программисту? Действительно, почему бы и нет.

Пример программы просмотра полей базы данных

Для того чтобы создать программу просмотра полей базы данных, вы должны кое-что узнать о внутреннем устройстве баз данных. Для этого вам понадобится база данных, с которой вы могли бы работать. В нашем примере мы предоставим пользователю возможность выбрать базу данных с диска. В данном случае мы наложим ограничение на тип базы данных это будет dBase, так что вам не придется волноваться по поводу установленных драйверов ODBC, но все будет работать точно так же и для любого другого типа баз данных из числа поддерживаемых CBuilder.

После того как пользователь выбрал базу данных, следующим шагом будет получение информации о полях. Для осуществления задуманного мы используем объект TTable (таблица) для открытия конкретного файла dBase, выбранного пользователем. Внутри объекта TTable вы и найдете информацию о полях и индексах, присутствующих в этой таблице. В первом примере мы не будем интересоваться индексами, а только информацией о полях.

Внутри объекта TTable содержится указатель на объект TFieldDefs. В этом объекте содержится массив объектов описания полей типа TFieldDef (один объект TFieldDefs содержит множество объектов TFieldDef). Этот объект содержит свойства, которые представляют информацию о каждом поле. Среди прочего, внутри этого объекта вы найдете имя, тип и размер поля. Имея всю эту информацию, мы можем приступить к созданию программы просмотра, используя форму, показанную на рис. 7.1.

Создаем пример программы просмотра полей

Создайте форму, показанную на рис. 7.1, добавив в пустую форму объекты TStringGrid, TMainMenu, TOpenDialog и TTable. В этом примере мы не будем напрямую устанавливать свойства объектов grid, dialog или table. Объект main menu должен содержать пункт Файл с подпунктами Открыть и Выход. Пункт меню Открыть будет использоваться для выбора таблицы базы данных для просмотра, а пункт Выход для того, чтобы закрыть приложение.

Рис. 7.1. Форма программы просмотра полей базы данных

Borland C++ Builder (+CD). Библиотека программиста 158

Первым шагом для отображения информации будет создание таблицы типов полей. Типы полей хранятся в базе данных как числовые значения типа TFldType. Нам бы хотелось иметь возможность отображать эти данные в более информатив ном виде, чтобы пользователь мог в них разобраться. Для осуществления подобного преобразования добавьте следующий код в начало исходного файла Unit1.h:

typedef struct

{

int nCode; char *strDesc; } DbFieldType;

DbFieldType sFieldTypes[] =

{

{ftUnknown, "Неизвестно или не определено"}, {ftString, "Символьное или строковое поле"}, {ftSmallint, "16-битное целое поле"}, {ftInteger, "32-битное целое поле"},

{ftWord, "16-битное беззнаковое целое поле"}, {ftBoolean, "Логическое поле"},

{ftFloat, "Поле чисел с плавающей точкой"}, {ftCurrency, "Денежное поле"},

{ftBCD, "Двоично-кодированное десятичное поле"}, {ftDate, "Поле даты"},

продолжение И

Глава 7•Работа с базами данных

{ftTime, "Поле времени"}, {ftDateTime, "Поле даты и времени"},

{ftBytes, "Фиксированное количество байт (двоичное представление)"},

{ftVarBytes, "Переменное количество байт (двоичное представление"},

{ftAutoInc, "Автоматически увеличивающееся 32-битное целое поле счетчика"},

{ftBlob, "Поле Binary Large Object (большой двоичный объект)"},

{ftMemo, "Поле memo (строка неограниченной длины)"}, {ftGraphic, "Поле растрового рисунка"},

{ftFmtMemo, "Поле форматированного memo"}, {ftParadoxOle, "Поле Paradox OLE"}, {ftDBaseOle, "Поле dBase OLE"},

{ftTypedBinary, "Типизированное двоичное поле"}, {-1,""} };

Эта таблица будет просто ставить в соответствие типы данных (символы ftxxx) и строки, которые характеризуют тип поля. Строки будут отображаться в сетке напротив каждого поля как тип этого поля.

Для того чтобы осуществить преобразование типа поля в его описание, нам придется также добавить в форму вспомогательную функцию. Назовем ее GetFieldTypeDescription. Эта функция получает один аргумент тип поля и возвращает строку, описывающую это поле. Вот код, который необходимо добавить в метод GetFieldTypeDescription:

Borland C++ Builder (+CD). Библиотека программиста 159

char *TForm1::GetFieldTypeDescription(int nInd)

{

for (int i=0; sFieldTypes[i].nCode != -1; ++i) if (sFieldTypes[i].nCode == nInd)

return sFieldTypes[i].strDesc; return "";

}

После того как типам полей поставлены в соответствие строки, которые и будут использоваться для вывода на экран, напишем обработчик открытия базы данных и загрузки в сетку информации из базы. Напишем обработчик для команды меню Файл|Открыть . В метод формы Open1Click (обработчик для этой команды меню) добавьте следующие строки:

void __fastcall TForm1::Open1Click(TObject *Sender)

{

OpenDialog1->Filter = "Файлы баз данных dBase|*.dbf"; if (OpenDialog1->Execute())

{

// Пытаемся открыть выбранную базу данных

Разбираемся во внутреннем устройстве базданных

Table1->DatabaseName = ExtractFilePath(OpenDialog1->FileName);

//Определяем имя таблицы

Table1->TableName = ExtractFileName(OpenDialog1->FileName);

//Делаем таблицу активной

Table1->Active = true;

//Загружаем сетку с частями таблицы

StringGrid1->RowCount = Table1->FieldCount; StringGrid1->ColCount = 4;

//Устанавливаем ширину столбцов

StringGrid1->ColWidth[0] = 30;

StringGrid1->ColWidth[1] = StringGrid1->ClientWidth/3 - 10; StringGrid1->ColWidth[2] = StringGrid1->ClientWidth/3 - 10; StringGrid1->ColWidth[3] = StringGrid1->ClientWidth/3 - 10; // Устанавливаем заголовки

StringGrid1->Cells[0][0] = "№ поля"; StringGrid1->Cells[0][0] = "Имя поля"; StringGrid1->Cells[0][0] = "Тип поля"; StringGrid1->Cells[0][0] = "Размер"; for (int i=1; i<Table1->FieldCount; ++i)

{

StringGrid1->Cells[0][i] = AnsiString(i); StringGrid1->Cells[1][i] = Table1->FieldDefs->Items[i]->Name; StringGrid1->Cells[2][i] = GetFieldTypeDescription(Table1-> FieldDefs->Items[i]->DataType); StringGrid1->Cells[3][i] = AnsiString(Table1->FieldDefs->

Borland C++ Builder (+CD). Библиотека программиста 160

Items[i]->Size);

}

}

}

Что же здесь происходит?

Некоторые строки приведенного выше кода как, например, касающиеся инициализации сетки или задания ширины колонок не представляют собой ничего слож

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

Две вспомогательные функции, определенные в заголовочном файле sysutils.hpp каталога include\vcl, используются для присвоения имени базы данных и имени таблицы. Имя базы данных для файла dBase — это просто каталог, в котором этот файл находится. Аналогично, имя таблицы

всего лишь имя файла, который вы собираетесь открыть.

Объект TOpenDialog возвращает полный путь к выбранному пользователем файлу в свойстве FileName. Мы знаем, что выбран файл корректного типа, ведь мы установили свойство Filter так, чтобы допустить только файлы dBase (DBF). Поэтому мы можем использовать вспомогательную функцию ExtractFilePath для того, чтобы получить только имя каталога, и вспомогательную функцию ExtractFileName для получения имени файла с выбранной таблицей. После того как обе функции были использованы, мы «открываем» базу данных, устанавливая свойство Active1 в true.

Замечание

Хотя это и не показано в примере, вышеприведенный код мог привести к возникновению исключительной ситуации. Если вы хотите сами обработать эту исключительную ситуацию, переустанавливайте свойство Active внутри блока try … catch. Можете делать что хотите внутри блока catch, чтобы дать понять пользователю, что возникла ошибка, и прервать исполнение метода. Если же вы ничего не сделаете, CBuilder автоматически обработает исключительную ситуацию и остановит работу программы.

После открытия базы данных мы устанавливаем число строк в сетке в значение, равное хранящемуся в свойстве FieldCount числу полей. Это свойство представляет количество полей, определенных в таблице, к которой вы обратились.

В случае файла dBase это то же самое, что и общее количество полей в таблице. После того как сетка инициирована, ширина и заголовки установлены, одно за другим опрашиваются поля и данные по каждому загружаются в ячейки клетки. Если вам не доводилось использовать класс TStringGrid ранее, не утруждайте себя обращением к справочной системе. На Web-сайте фирмы Borland находится обновленный файл помощи, который содержит информацию о сетке и нескольких других классах, случайно обойденных вниманием как в справочной системе, так и в бумажной документации. В данном примере мы используем свойство TStringGrid, называемое Cells (ячейки) для установки индивидуальных атрибутов строк и столбцов сетки. Если последнее утверждение показалось вам забавным, не волнуйтесь не вам одному; свойство Cells работает в формате столбец, строка.

Так же как это реализовано во многих других элементах системы, в объекте FieldDefs отдельные поля хранятся в свойстве Items (части, пункты). В свойстве Items вы найдете информацию об имени поля (свойство Name), типе поля (свойство DataField, которое мы преобразуем в строку при помощи метода utility) и размере поля (свойство Size).

Соседние файлы в предмете Программирование на C++